author | Wes Kocher <wkocher@mozilla.com> |
Mon, 25 Aug 2014 17:19:50 -0700 | |
changeset 201520 | dc352a7bf234df7682ceedbf70b1f676e82e8171 |
parent 201519 | 0736ab6957b2de9c87c05e41be8c356dcb1bf3b1 (current diff) |
parent 201489 | 156f1bee413918f0296c648075c5b4de6aa95478 (diff) |
child 201521 | 0843c26857a9d0534a461ec3fdacfd8b6112167e |
child 201635 | 145df5e2de3d7de995e59ebea3e09f45fc2c39de |
child 201687 | be6073a0d24a2c504c0b137a1363e686b054c8ac |
child 204782 | 0b020d3a84b90f3ace790e5de01ef9a0e45a4da5 |
push id | 48191 |
push user | kwierso@gmail.com |
push date | Tue, 26 Aug 2014 00:25:28 +0000 |
treeherder | mozilla-inbound@0843c26857a9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 34.0a1 |
first release with | nightly linux32
dc352a7bf234
/
34.0a1
/
20140826030203
/
files
nightly linux64
dc352a7bf234
/
34.0a1
/
20140826030203
/
files
nightly mac
dc352a7bf234
/
34.0a1
/
20140826030203
/
files
nightly win32
dc352a7bf234
/
34.0a1
/
20140826030203
/
files
nightly win64
dc352a7bf234
/
34.0a1
/
20140826030203
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
34.0a1
/
20140826030203
/
pushlog to previous
nightly linux64
34.0a1
/
20140826030203
/
pushlog to previous
nightly mac
34.0a1
/
20140826030203
/
pushlog to previous
nightly win32
34.0a1
/
20140826030203
/
pushlog to previous
nightly win64
34.0a1
/
20140826030203
/
pushlog to previous
|
--- a/accessible/jsat/AccessFu.jsm +++ b/accessible/jsat/AccessFu.jsm @@ -243,17 +243,17 @@ this.AccessFu = { // jshint ignore:line try { Output[presenter.type](presenter.details, aBrowser); } catch (x) { Logger.logException(x); } } if (this._notifyOutputPref.value) { - Services.obs.notifyObservers(null, 'accessfu-output', + Services.obs.notifyObservers(null, 'accessibility-output', JSON.stringify(aPresentationData)); } }, _loadFrameScript: function _loadFrameScript(aMessageManager) { if (this._processedMessageManagers.indexOf(aMessageManager) < 0) { aMessageManager.loadFrameScript( 'chrome://global/content/accessibility/content-script.js', true); @@ -509,17 +509,17 @@ var Output = { stop: function stop() { if (this.highlightBox) { Utils.win.document.documentElement.removeChild(this.highlightBox.get()); delete this.highlightBox; } }, B2G: function B2G(aDetails) { - Utils.dispatchChromeEvent('accessfu-output', aDetails); + Utils.dispatchChromeEvent('accessibility-output', aDetails); }, Visual: function Visual(aDetail, aBrowser) { switch (aDetail.eventType) { case 'viewport-change': case 'vc-change': { let highlightBox = null;
--- a/accessible/tests/mochitest/jsat/jsatcommon.js +++ b/accessible/tests/mochitest/jsat/jsatcommon.js @@ -76,31 +76,31 @@ var AccessFuTest = { var data = JSON.parse(aData)[1]; // Ignore non-relevant outputs. if (!data) { return; } isDeeply(data.details, aWaitForData, "Data is correct"); aListener.apply(listener); }; - Services.obs.addObserver(listener, 'accessfu-output', false); + Services.obs.addObserver(listener, 'accessibility-output', false); return listener; }, on: function AccessFuTest_on(aWaitForData, aListener) { return this._addObserver(aWaitForData, aListener); }, off: function AccessFuTest_off(aListener) { - Services.obs.removeObserver(aListener, 'accessfu-output'); + Services.obs.removeObserver(aListener, 'accessibility-output'); }, once: function AccessFuTest_once(aWaitForData, aListener) { return this._addObserver(aWaitForData, function observerAndRemove() { - Services.obs.removeObserver(this, 'accessfu-output'); + Services.obs.removeObserver(this, 'accessibility-output'); aListener(); }); }, _waitForExplicitFinish: false, waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() { this._waitForExplicitFinish = true;
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -49,17 +49,17 @@ pref("browser.cache.disk.smart_size.firs pref("browser.cache.memory.enable", true); pref("browser.cache.memory.capacity", 1024); // kilobytes pref("browser.cache.memory_limit", 2048); // 2 MB /* image cache prefs */ pref("image.cache.size", 1048576); // bytes pref("image.high_quality_downscaling.enabled", false); -pref("canvas.image.cache.limit", 10485760); // 10 MB +pref("canvas.image.cache.limit", 20971520); // 20 MB /* offline cache prefs */ pref("browser.offline-apps.notify", false); pref("browser.cache.offline.enable", true); pref("offline-apps.allow_by_default", true); /* protocol warning prefs */ pref("network.protocol-handler.warn-external.tel", false);
--- a/browser/base/content/browser-plugins.js +++ b/browser/base/content/browser-plugins.js @@ -1217,23 +1217,25 @@ var gPluginHandler = { // If we're showing the link to manually trigger report submission, we'll // want to be able to update all the instances of the UI for this crash to // show an updated message when a report is submitted. if (doPrompt) { let observer = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), - observe : function(subject, topic, data) { + observe : (subject, topic, data) => { let propertyBag = subject; if (!(propertyBag instanceof Ci.nsIPropertyBag2)) return; // Ignore notifications for other crashes. if (propertyBag.get("minidumpID") != pluginDumpID) return; + + let statusDiv = this.getPluginUI(plugin, "submitStatus"); statusDiv.setAttribute("status", data); }, handleEvent : function(event) { // Not expected to be called, just here for the closure. } }
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -1163,23 +1163,23 @@ nsContextMenu.prototype = { const promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"]. getService(Ci.nsIPromptService); promptSvc.alert(doc.defaultView, title, msg); } catch (ex) {} return; } - var extHelperAppSvc = + let extHelperAppSvc = Cc["@mozilla.org/uriloader/external-helper-app-service;1"]. getService(Ci.nsIExternalHelperAppService); - var channel = aRequest.QueryInterface(Ci.nsIChannel); - this.extListener = + let channel = aRequest.QueryInterface(Ci.nsIChannel); + this.extListener = extHelperAppSvc.doContent(channel.contentType, aRequest, - doc.defaultView, true); + doc.defaultView, true, window); this.extListener.onStartRequest(aRequest, aContext); }, onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext, aStatusCode) { if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) { // do it the old fashioned way, which will pick the best filename // it can without waiting.
--- a/browser/base/content/test/plugins/browser_CTP_crashreporting.js +++ b/browser/base/content/test/plugins/browser_CTP_crashreporting.js @@ -109,16 +109,25 @@ function onSubmitStatus(subj, topic, dat let val = getPropertyBagValue(extra, "PluginUserComment"); is(val, "a test comment", "Comment in extra data should match comment in textbox"); val = getPropertyBagValue(extra, "PluginContentURL"); ok(val === undefined, "URL should be absent from extra data when opt-in not checked"); + + // Execute this later in case the event to change submitStatus has not + // have been dispatched yet. + executeSoon(function () { + let plugin = gBrowser.contentDocument.getElementById("test"); + let elt = gPluginHandler.getPluginUI.bind(gPluginHandler, plugin); + is(elt("submitStatus").getAttribute("status"), data, + "submitStatus data should match"); + }); } catch (err) { failWithException(err); } finish(); } function getPropertyBagValue(bag, key) {
--- a/build/gyp.mozbuild +++ b/build/gyp.mozbuild @@ -104,17 +104,17 @@ if CONFIG['ARM_ARCH']: gyp_vars['arm_neon_optional'] = 0 elif os == 'Android': gyp_vars['armv7'] = 1 else: # CPU detection for ARM works on Android only. armv7 always uses CPU # detection, so we have to set armv7=0 for non-Android target gyp_vars['armv7'] = 0 # For libyuv - gyp_vars['arm_version'] = CONFIG['ARM_ARCH'] + gyp_vars['arm_version'] = int(CONFIG['ARM_ARCH']) # Don't try to compile ssse3/sse4.1 code if toolchain doesn't support if CONFIG['INTEL_ARCHITECTURE']: if not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSSE3'] or not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSE4_1']: gyp_vars['yuv_disable_asm'] = 1 if CONFIG['MACOS_SDK_DIR']: gyp_vars['mac_sdk_path'] = CONFIG['MACOS_SDK_DIR']
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -7496,21 +7496,27 @@ nsIDocument::AdoptNode(nsINode& aAdopted "Should still be in the document we just got adopted into"); return adoptedNode; } nsViewportInfo nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize) { + // Compute the CSS-to-LayoutDevice pixel scale as the product of the + // widget scale and the full zoom. nsPresContext* context = mPresShell->GetPresContext(); float fullZoom = context ? context->GetFullZoom() : 1.0; fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom; - CSSToScreenScale defaultScale = CSSToLayoutDeviceScale(fullZoom) * - LayoutDeviceToScreenScale(1.0); + nsIWidget *widget = nsContentUtils::WidgetForDocument(this); + float widgetScale = widget ? widget->GetDefaultScale().scale : 1.0f; + CSSToLayoutDeviceScale layoutDeviceScale(widgetScale * fullZoom); + + CSSToScreenScale defaultScale = layoutDeviceScale + * LayoutDeviceToScreenScale(1.0); // In cases where the width of the CSS viewport is less than or equal to the width // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom // behaviour. See bug 941995 for details. switch (mViewportType) { case DisplayWidthHeight: return nsViewportInfo(aDisplaySize, @@ -7684,29 +7690,23 @@ nsDocument::GetViewportInfo(const Screen if (!mValidHeight) { if (!aDisplaySize.IsEmpty()) { size.height = size.width * aDisplaySize.height / aDisplaySize.width; } else { size.height = size.width; } } - // Now convert the scale into device pixels per CSS pixel base on this formula - // CSSPixel x widget scale x full zoom = LayoutDevicePixel - nsIWidget *widget = nsContentUtils::WidgetForDocument(this); - CSSToLayoutDeviceScale pixelRatio = CSSToLayoutDeviceScale( - (widget ? widget->GetDefaultScale().scale : 1.0f) * fullZoom); - - CSSToScreenScale scaleFloat = mScaleFloat * pixelRatio; - CSSToScreenScale scaleMinFloat = mScaleMinFloat * pixelRatio; - CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * pixelRatio; + CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale; + CSSToScreenScale scaleMinFloat = mScaleMinFloat * layoutDeviceScale; + CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * layoutDeviceScale; if (mAutoSize) { // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size. - CSSToScreenScale defaultPixelScale = pixelRatio * LayoutDeviceToScreenScale(1.0f); + CSSToScreenScale defaultPixelScale = layoutDeviceScale * LayoutDeviceToScreenScale(1.0f); size = ScreenSize(aDisplaySize) / defaultPixelScale; } size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width)); // Also recalculate the default zoom, if it wasn't specified in the metadata, // and the width is specified. if (mScaleStrEmpty && !mWidthStrEmpty) {
--- a/content/html/content/src/HTMLSelectElement.cpp +++ b/content/html/content/src/HTMLSelectElement.cpp @@ -968,18 +968,18 @@ HTMLSelectElement::SetOptionsSelectedByI bool optionsDeselected = false; nsISelectControlFrame* selectFrame = nullptr; bool didGetFrame = false; nsWeakFrame weakSelectFrame; if (aOptionsMask & IS_SELECTED) { // Setting selectedIndex to an out-of-bounds index means -1. (HTML5) - if (aStartIndex < 0 || SafeCast<uint32_t>(aStartIndex) >= numItems || - aEndIndex < 0 || SafeCast<uint32_t>(aEndIndex) >= numItems) { + if (aStartIndex < 0 || AssertedCast<uint32_t>(aStartIndex) >= numItems || + aEndIndex < 0 || AssertedCast<uint32_t>(aEndIndex) >= numItems) { aStartIndex = -1; aEndIndex = -1; } // Only select the first value if it's not multiple if (!isMultiple) { aEndIndex = aStartIndex; } @@ -999,18 +999,18 @@ HTMLSelectElement::SetOptionsSelectedByI // Select the requested indices // // If index is -1, everything will be deselected (bug 28143) if (aStartIndex != -1) { MOZ_ASSERT(aStartIndex >= 0); MOZ_ASSERT(aEndIndex >= 0); // Loop through the options and select them (if they are not disabled and // if they are not already selected). - for (uint32_t optIndex = SafeCast<uint32_t>(aStartIndex); - optIndex <= SafeCast<uint32_t>(aEndIndex); + for (uint32_t optIndex = AssertedCast<uint32_t>(aStartIndex); + optIndex <= AssertedCast<uint32_t>(aEndIndex); optIndex++) { nsRefPtr<HTMLOptionElement> option = Item(optIndex); // Ignore disabled options. if (!(aOptionsMask & SET_DISABLED)) { if (option && IsOptionDisabled(option)) { continue; } @@ -1035,17 +1035,17 @@ HTMLSelectElement::SetOptionsSelectedByI } // Next remove all other options if single select or all is clear // If index is -1, everything will be deselected (bug 28143) if (((!isMultiple && optionsSelected) || ((aOptionsMask & CLEAR_ALL) && !allDisabled) || aStartIndex == -1) && previousSelectedIndex != -1) { - for (uint32_t optIndex = SafeCast<uint32_t>(previousSelectedIndex); + for (uint32_t optIndex = AssertedCast<uint32_t>(previousSelectedIndex); optIndex < numItems; optIndex++) { if (static_cast<int32_t>(optIndex) < aStartIndex || static_cast<int32_t>(optIndex) > aEndIndex) { HTMLOptionElement* option = Item(optIndex); // If the index is already selected, ignore it. if (option && option->Selected()) { if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
--- a/content/media/gmp/GMPChild.cpp +++ b/content/media/gmp/GMPChild.cpp @@ -121,30 +121,84 @@ GetPluginPaths(const std::string& aPlugi libFile->GetNativeTarget(aPluginFilePath); } else { libFile->GetNativePath(aPluginFilePath); } return true; } +static bool +GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath) +{ + nsAutoCString appPath; + nsAutoCString appBinaryPath( + (CommandLine::ForCurrentProcess()->argv()[0]).c_str()); + + nsAutoCString::const_iterator start, end; + appBinaryPath.BeginReading(start); + appBinaryPath.EndReading(end); + if (RFindInReadable(NS_LITERAL_CSTRING(".app/Contents/MacOS/"), start, end)) { + end = start; + ++end; ++end; ++end; ++end; + appBinaryPath.BeginReading(start); + appPath.Assign(Substring(start, end)); + } else { + return false; + } + + nsCOMPtr<nsIFile> app, appBinary; + nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appPath), + true, getter_AddRefs(app)); + if (NS_FAILED(rv)) { + return false; + } + rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appBinaryPath), + true, getter_AddRefs(appBinary)); + if (NS_FAILED(rv)) { + return false; + } + + bool isLink; + app->IsSymlink(&isLink); + if (isLink) { + app->GetNativeTarget(aAppPath); + } else { + app->GetNativePath(aAppPath); + } + appBinary->IsSymlink(&isLink); + if (isLink) { + appBinary->GetNativeTarget(aAppBinaryPath); + } else { + appBinary->GetNativePath(aAppBinaryPath); + } + + return true; +} + void GMPChild::OnChannelConnected(int32_t aPid) { nsAutoCString pluginDirectoryPath, pluginFilePath; if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) { MOZ_CRASH("Error scanning plugin path"); } + nsAutoCString appPath, appBinaryPath; + if (!GetAppPaths(appPath, appBinaryPath)) { + MOZ_CRASH("Error resolving child process path"); + } MacSandboxInfo info; info.type = MacSandboxType_Plugin; info.pluginInfo.type = MacSandboxPluginType_GMPlugin_Default; info.pluginInfo.pluginPath.Assign(pluginDirectoryPath); mPluginBinaryPath.Assign(pluginFilePath); info.pluginInfo.pluginBinaryPath.Assign(pluginFilePath); + info.appPath.Assign(appPath); + info.appBinaryPath.Assign(appBinaryPath); nsAutoCString err; if (!mozilla::StartMacSandbox(info, err)) { NS_WARNING(err.get()); MOZ_CRASH("sandbox_init() failed"); } if (!LoadPluginLibrary(mPluginPath)) {
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -872,17 +872,17 @@ nsDocShell::nsDocShell(): #ifdef DEBUG // We're counting the number of |nsDocShells| to help find leaks ++gNumberOfDocShells; if (!PR_GetEnv("MOZ_QUIET")) { printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %llu]\n", (void*) this, gNumberOfDocShells, getpid(), - SafeCast<unsigned long long>(mHistoryID)); + AssertedCast<unsigned long long>(mHistoryID)); } #endif } nsDocShell::~nsDocShell() { Destroy(); @@ -904,17 +904,17 @@ nsDocShell::~nsDocShell() #ifdef DEBUG // We're counting the number of |nsDocShells| to help find leaks --gNumberOfDocShells; if (!PR_GetEnv("MOZ_QUIET")) { printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %llu]\n", (void*) this, gNumberOfDocShells, getpid(), - SafeCast<unsigned long long>(mHistoryID)); + AssertedCast<unsigned long long>(mHistoryID)); } #endif } nsresult nsDocShell::Init() { nsresult rv = nsDocLoader::Init();
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1030,16 +1030,22 @@ class CGHeaders(CGWrapper): elif unrolled.isDate(): if dictionary or jsImplementedDescriptors: declareIncludes.add("mozilla/dom/Date.h") else: bindingHeaders.add("mozilla/dom/Date.h") elif unrolled.isInterface(): if unrolled.isSpiderMonkeyInterface(): bindingHeaders.add("jsfriendapi.h") + if jsImplementedDescriptors: + # Since we can't forward-declare typed array types + # (because they're typedefs), we have to go ahead and + # just include their header if we need to have functions + # taking references to them declared in that header. + headerSet = declareIncludes headerSet.add("mozilla/dom/TypedArray.h") else: providers = getRelevantProviders(descriptor, config) for p in providers: try: typeDesc = p.getDescriptor(unrolled.inner.identifier.name) except NoSuchDescriptorError: continue @@ -5480,17 +5486,18 @@ def getWrapTemplateForType(type, descrip type.inner, descriptorProvider, { 'result': "%s[%s]" % (result, index), 'successCode': "break;\n", 'jsvalRef': "tmp", 'jsvalHandle': "&tmp", 'returnsNewObject': returnsNewObject, 'exceptionCode': exceptionCode, - 'obj': "returnArray" + 'obj': "returnArray", + 'typedArraysAreStructs': typedArraysAreStructs }) sequenceWrapLevel -= 1 code = fill( """ uint32_t length = ${result}.Length(); JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length)); if (!returnArray) { @@ -5533,17 +5540,18 @@ def getWrapTemplateForType(type, descrip type.inner, descriptorProvider, { 'result': valueName, 'successCode': "break;\n", 'jsvalRef': "tmp", 'jsvalHandle': "&tmp", 'returnsNewObject': returnsNewObject, 'exceptionCode': exceptionCode, - 'obj': "returnObj" + 'obj': "returnObj", + 'typedArraysAreStructs': typedArraysAreStructs }) mozMapWrapLevel -= 1 code = fill( """ nsTArray<nsString> keys; ${result}.GetKeys(keys); JS::Rooted<JSObject*> returnObj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); @@ -11804,16 +11812,18 @@ class CGForwardDeclarations(CGWrapper): pass # Find and add the worker implementation, if any. if workerness != 'mainthreadonly': try: desc = config.getDescriptor(name, True) builder.add(desc.nativeType) except NoSuchDescriptorError: pass + # Note: Spidermonkey interfaces are typedefs, so can't be + # forward-declared elif t.isCallback(): builder.addInMozillaDom(str(t)) elif t.isDictionary(): builder.addInMozillaDom(t.inner.identifier.name, isStruct=True) elif t.isCallbackInterface(): builder.addInMozillaDom(t.inner.identifier.name) elif t.isUnion(): # Forward declare both the owning and non-owning version, @@ -12010,17 +12020,17 @@ class CGBindingRoot(CGThing): # Do codegen for JS implemented classes def getParentDescriptor(desc): if not desc.interface.parent: return set() return {desc.getDescriptor(desc.interface.parent.identifier.name)} for x in dependencySortObjects(jsImplemented, getParentDescriptor, lambda d: d.interface.identifier.name): - cgthings.append(CGCallbackInterface(x)) + cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True)) cgthings.append(CGJSImplClass(x)) # And make sure we have the right number of newlines at the end curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") # Wrap all of that in our namespaces. curr = CGNamespace.build(['mozilla', 'dom'], CGWrapper(curr, pre="\n")) @@ -12068,31 +12078,32 @@ class CGBindingRoot(CGThing): def deps(self): return self.root.deps() class CGNativeMember(ClassMethod): def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True, visibility="public", - jsObjectsArePtr=False, variadicIsSequence=False): + typedArraysAreStructs=True, variadicIsSequence=False): """ - If jsObjectsArePtr is true, typed arrays and "object" will be - passed as JSObject*. + If typedArraysAreStructs is false, typed arrays will be passed as + JS::Handle<JSObject*>. If it's true they will be passed as one of the + dom::TypedArray subclasses. If passJSBitsAsNeeded is false, we don't automatically pass in a JSContext* or a JSObject* based on the return and argument types. We can still pass it based on 'implicitJSContext' annotations. """ self.descriptorProvider = descriptorProvider self.member = member self.extendedAttrs = extendedAttrs self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.extendedAttrs) self.passJSBitsAsNeeded = passJSBitsAsNeeded - self.jsObjectsArePtr = jsObjectsArePtr + self.typedArraysAreStructs = typedArraysAreStructs self.variadicIsSequence = variadicIsSequence breakAfterSelf = "\n" if breakAfter else "" ClassMethod.__init__(self, name, self.getReturnType(signature[0], False), self.getArgs(signature[0], signature[1]), static=member.isStatic(), # Mark our getters, which are attrs that # have a non-void return type, as const. @@ -12190,19 +12201,19 @@ class CGNativeMember(ClassMethod): # No need for a third element in the isMember case return "JSObject*", None, None return "void", "", "aRetVal.set(${declName});\n" if type.isSpiderMonkeyInterface(): if isMember: # No need for a third element in the isMember case return "JSObject*", None, None if type.nullable(): - returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();\n" - else: - returnCode = "${declName}.Obj();\n" + returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()" + else: + returnCode = "${declName}.Obj()" return "void", "", "aRetVal.set(%s);\n" % returnCode if type.isSequence(): # If we want to handle sequence-of-sequences return values, we're # going to need to fix example codegen to not produce nsTArray<void> # for the relevant argument... assert not isMember # Outparam. if type.nullable(): @@ -12368,18 +12379,18 @@ class CGNativeMember(ClassMethod): typeDecl = "NonNull<%s>" else: typeDecl = "%s&" return ((typeDecl % self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType), False, False) if type.isSpiderMonkeyInterface(): - if self.jsObjectsArePtr: - return "JSObject*", False, False + if not self.typedArraysAreStructs: + return "JS::Handle<JSObject*>", False, False return type.name, True, True if type.isDOMString() or type.isScalarValueString(): if isMember: declType = "nsString" else: declType = "nsAString" @@ -12857,23 +12868,21 @@ def jsImplName(name): class CGJSImplMember(CGNativeMember): """ Base class for generating code for the members of the implementation class for a JS-implemented WebIDL interface. """ def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True, - visibility="public", jsObjectsArePtr=False, - variadicIsSequence=False): + visibility="public", variadicIsSequence=False): CGNativeMember.__init__(self, descriptorProvider, member, name, signature, extendedAttrs, breakAfter=breakAfter, passJSBitsAsNeeded=passJSBitsAsNeeded, visibility=visibility, - jsObjectsArePtr=jsObjectsArePtr, variadicIsSequence=variadicIsSequence) self.body = self.getImpl() def getArgs(self, returnType, argList): args = CGNativeMember.getArgs(self, returnType, argList) args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) return args @@ -13365,26 +13374,27 @@ class CGCallbackFunction(CGCallback): [Argument("CallbackFunction*", "aOther")], bodyInHeader=True, visibility="public", explicit=True, baseConstructors=["CallbackFunction(aOther)"])] class CGCallbackInterface(CGCallback): - def __init__(self, descriptor): + def __init__(self, descriptor, typedArraysAreStructs=False): iface = descriptor.interface attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] - getters = [CallbackGetter(a, descriptor) for a in attrs] - setters = [CallbackSetter(a, descriptor) for a in attrs - if not a.readonly] + getters = [CallbackGetter(a, descriptor, typedArraysAreStructs) + for a in attrs] + setters = [CallbackSetter(a, descriptor, typedArraysAreStructs) + for a in attrs if not a.readonly] methods = [m for m in iface.members if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] - methods = [CallbackOperation(m, sig, descriptor) for m in methods - for sig in m.signatures()] + methods = [CallbackOperation(m, sig, descriptor, typedArraysAreStructs) + for m in methods for sig in m.signatures()] if iface.isJSImplemented() and iface.ctor(): sigs = descriptor.interface.ctor().signatures() if len(sigs) != 1: raise TypeError("We only handle one constructor. See bug 869268.") methods.append(CGJSImplInitOperation(sigs[0], descriptor)) if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()): methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name) for m in iface.members @@ -13415,17 +13425,18 @@ class FakeMember(): # resultNotAddRefed" comments CGNativeMember codegen would # otherwise stick in. if name == "NewObject": return True return None class CallbackMember(CGNativeMember): - def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False): + def __init__(self, sig, name, descriptorProvider, needThisHandling, + rethrowContentException=False, typedArraysAreStructs=False): """ needThisHandling is True if we need to be able to accept a specified thisObj, False otherwise. """ assert not rethrowContentException or not needThisHandling self.retvalType = sig[0] self.originalSig = sig @@ -13447,17 +13458,17 @@ class CallbackMember(CGNativeMember): # We don't care, for callback codegen, whether our original member was # a method or attribute or whatnot. Just always pass FakeMember() # here. CGNativeMember.__init__(self, descriptorProvider, FakeMember(), name, (self.retvalType, args), extendedAttrs={}, passJSBitsAsNeeded=False, visibility=visibility, - jsObjectsArePtr=True) + typedArraysAreStructs=typedArraysAreStructs) # We have to do all the generation of our body now, because # the caller relies on us throwing if we can't manage it. self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n" "return%s;\n" % self.getDefaultRetval()) self.body = self.getImpl() def getImpl(self): setupCall = self.getCallSetup() @@ -13563,17 +13574,18 @@ class CallbackMember(CGNativeMember): 'successCode': "continue;\n" if arg.variadic else "break;\n", 'jsvalRef': "argv[%s]" % jsvalIndex, 'jsvalHandle': "argv[%s]" % jsvalIndex, # XXXbz we don't have anything better to use for 'obj', # really... It's OK to use CallbackPreserveColor because # CallSetup already handled the unmark-gray bits for us. 'obj': 'CallbackPreserveColor()', 'returnsNewObject': False, - 'exceptionCode': self.exceptionCode + 'exceptionCode': self.exceptionCode, + 'typedArraysAreStructs': self.typedArraysAreStructs }) except MethodNotNewObjectError as err: raise TypeError("%s being passed as an argument to %s but is not " "wrapper cached, so can't be reliably converted to " "a JS object." % (err.typename, self.getPrettyName())) if arg.variadic: conversion = fill( @@ -13666,19 +13678,20 @@ class CallbackMember(CGNativeMember): "double-quote character. We can't handle " "that. %s" % (type, idlObject.identifier.name, idlObject.location)) class CallbackMethod(CallbackMember): def __init__(self, sig, name, descriptorProvider, needThisHandling, - rethrowContentException=False): + rethrowContentException=False, typedArraysAreStructs=False): CallbackMember.__init__(self, sig, name, descriptorProvider, - needThisHandling, rethrowContentException) + needThisHandling, rethrowContentException, + typedArraysAreStructs=typedArraysAreStructs) def getRvalDecl(self): return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" def getCall(self): if self.argCount > 0: args = "JS::HandleValueArray::subarray(argv, 0, argc)" else: @@ -13725,20 +13738,24 @@ class CallCallback(CallbackMethod): return "JS_ObjectIsCallable(cx, mCallback) && " return "" class CallbackOperationBase(CallbackMethod): """ Common class for implementing various callback operations. """ - def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False): + def __init__(self, signature, jsName, nativeName, descriptor, + singleOperation, rethrowContentException=False, + typedArraysAreStructs=False): self.singleOperation = singleOperation self.methodName = descriptor.binaryNameFor(jsName) - CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException) + CallbackMethod.__init__(self, signature, nativeName, descriptor, + singleOperation, rethrowContentException, + typedArraysAreStructs=typedArraysAreStructs) def getThisDecl(self): if not self.singleOperation: return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n" # This relies on getCallableDecl declaring a boolean # isCallable in the case when we're a single-operation # interface. return dedent(""" @@ -13779,53 +13796,56 @@ class CallbackOperationBase(CallbackMeth def getCallGuard(self): return "" class CallbackOperation(CallbackOperationBase): """ Codegen actual WebIDL operations on callback interfaces. """ - def __init__(self, method, signature, descriptor): + def __init__(self, method, signature, descriptor, typedArraysAreStructs): self.ensureASCIIName(method) self.method = method jsName = method.identifier.name CallbackOperationBase.__init__(self, signature, jsName, MakeNativeName(descriptor.binaryNameFor(jsName)), descriptor, descriptor.interface.isSingleOperationInterface(), - rethrowContentException=descriptor.interface.isJSImplemented()) + rethrowContentException=descriptor.interface.isJSImplemented(), + typedArraysAreStructs=typedArraysAreStructs) def getPrettyName(self): return "%s.%s" % (self.descriptorProvider.interface.identifier.name, self.method.identifier.name) class CallbackAccessor(CallbackMember): """ Shared superclass for CallbackGetter and CallbackSetter. """ - def __init__(self, attr, sig, name, descriptor): + def __init__(self, attr, sig, name, descriptor, typedArraysAreStructs): self.ensureASCIIName(attr) self.attrName = attr.identifier.name CallbackMember.__init__(self, sig, name, descriptor, needThisHandling=False, - rethrowContentException=descriptor.interface.isJSImplemented()) + rethrowContentException=descriptor.interface.isJSImplemented(), + typedArraysAreStructs=typedArraysAreStructs) def getPrettyName(self): return "%s.%s" % (self.descriptorProvider.interface.identifier.name, self.attrName) class CallbackGetter(CallbackAccessor): - def __init__(self, attr, descriptor): + def __init__(self, attr, descriptor, typedArraysAreStructs): CallbackAccessor.__init__(self, attr, (attr.type, []), callbackGetterName(attr, descriptor), - descriptor) + descriptor, + typedArraysAreStructs) def getRvalDecl(self): return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" def getCall(self): return fill( """ JS::Rooted<JSObject *> callback(cx, mCallback); @@ -13837,22 +13857,22 @@ class CallbackGetter(CallbackAccessor): } """, atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms", attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)), errorReturn=self.getDefaultRetval()) class CallbackSetter(CallbackAccessor): - def __init__(self, attr, descriptor): + def __init__(self, attr, descriptor, typedArraysAreStructs): CallbackAccessor.__init__(self, attr, (BuiltinTypes[IDLBuiltinType.Types.void], [FakeArgument(attr.type, attr)]), callbackSetterName(attr, descriptor), - descriptor) + descriptor, typedArraysAreStructs) def getRvalDecl(self): # We don't need an rval return "" def getCall(self): return fill( """
--- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1002,17 +1002,24 @@ class IDLInterface(IDLObjectWithScope): [attr.location]) iface = forwardIface attr = fowardAttr putForwards = attr.getExtendedAttribute("PutForwards") if (self.getExtendedAttribute("Pref") and self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): - raise WebIDLError("[Pref] used on an member that is not %s-only" % + raise WebIDLError("[Pref] used on an interface that is not %s-only" % + self.parentScope.primaryGlobalName, + [self.location]) + + if (self.getExtendedAttribute("CheckPermissions") and + self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): + raise WebIDLError("[CheckPermissions] used on an interface that is " + "not %s-only" % self.parentScope.primaryGlobalName, [self.location]) def isInterface(self): return True def isExternal(self): @@ -3017,16 +3024,23 @@ class IDLInterfaceMember(IDLObjectWithId def validate(self): if (self.getExtendedAttribute("Pref") and self.exposureSet != set([self._scope.primaryGlobalName])): raise WebIDLError("[Pref] used on an interface member that is not " "%s-only" % self._scope.primaryGlobalName, [self.location]) + if (self.getExtendedAttribute("CheckPermissions") and + self.exposureSet != set([self._scope.primaryGlobalName])): + raise WebIDLError("[CheckPermissions] used on an interface member " + "that is not %s-only" % + self._scope.primaryGlobalName, + [self.location]) + class IDLConst(IDLInterfaceMember): def __init__(self, location, identifier, type, value): IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.Tags.Const) assert isinstance(type, IDLType) if type.isDictionary(): raise WebIDLError("A constant cannot be of a dictionary type",
--- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -456,16 +456,18 @@ public: void PassFloat64Array(const Float64Array&); void PassSequenceOfArrayBuffers(const Sequence<ArrayBuffer>&); void PassSequenceOfNullableArrayBuffers(const Sequence<Nullable<ArrayBuffer> >&); void PassMozMapOfArrayBuffers(const MozMap<ArrayBuffer>&); void PassMozMapOfNullableArrayBuffers(const MozMap<Nullable<ArrayBuffer> >&); void PassVariadicTypedArray(const Sequence<Float32Array>&); void PassVariadicNullableTypedArray(const Sequence<Nullable<Float32Array> >&); void ReceiveUint8Array(JSContext*, JS::MutableHandle<JSObject*>); + void SetUint8ArrayAttr(const Uint8Array&); + void GetUint8ArrayAttr(JSContext*, JS::MutableHandle<JSObject*>); // DOMString types void PassString(const nsAString&); void PassNullableString(const nsAString&); void PassOptionalString(const Optional<nsAString>&); void PassOptionalStringWithDefaultValue(const nsAString&); void PassOptionalNullableString(const Optional<nsAString>&); void PassOptionalNullableStringWithDefaultValue(const nsAString&);
--- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -28,16 +28,37 @@ callback interface TestCallbackInterface sequence<TestInterface?> getSequenceOfNullableInterfaces(); sequence<TestInterface?>? getNullableSequenceOfNullableInterfaces(); sequence<TestCallbackInterface> getSequenceOfCallbackInterfaces(); sequence<TestCallbackInterface>? getNullableSequenceOfCallbackInterfaces(); sequence<TestCallbackInterface?> getSequenceOfNullableCallbackInterfaces(); sequence<TestCallbackInterface?>? getNullableSequenceOfNullableCallbackInterfaces(); MozMap<long> getMozMapOfLong(); Dict? getDictionary(); + void passArrayBuffer(ArrayBuffer arg); + void passNullableArrayBuffer(ArrayBuffer? arg); + void passOptionalArrayBuffer(optional ArrayBuffer arg); + void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg); + void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg= null); + void passArrayBufferView(ArrayBufferView arg); + void passInt8Array(Int8Array arg); + void passInt16Array(Int16Array arg); + void passInt32Array(Int32Array arg); + void passUint8Array(Uint8Array arg); + void passUint16Array(Uint16Array arg); + void passUint32Array(Uint32Array arg); + void passUint8ClampedArray(Uint8ClampedArray arg); + void passFloat32Array(Float32Array arg); + void passFloat64Array(Float64Array arg); + void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg); + void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg); + void passVariadicTypedArray(Float32Array... arg); + void passVariadicNullableTypedArray(Float32Array?... arg); + Uint8Array receiveUint8Array(); + attribute Uint8Array uint8ArrayAttr; }; callback interface TestSingleOperationCallbackInterface { TestInterface doSomething(short arg, sequence<double> anotherArg); }; enum TestEnum { "1", @@ -415,16 +436,17 @@ interface TestInterface { void passFloat64Array(Float64Array arg); void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg); void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg); void passMozMapOfArrayBuffers(MozMap<ArrayBuffer> arg); void passMozMapOfNullableArrayBuffers(MozMap<ArrayBuffer?> arg); void passVariadicTypedArray(Float32Array... arg); void passVariadicNullableTypedArray(Float32Array?... arg); Uint8Array receiveUint8Array(); + attribute Uint8Array uint8ArrayAttr; // DOMString types void passString(DOMString arg); void passNullableString(DOMString? arg); void passOptionalString(optional DOMString arg); void passOptionalStringWithDefaultValue(optional DOMString arg = "abc"); void passOptionalNullableString(optional DOMString? arg); void passOptionalNullableStringWithDefaultValue(optional DOMString? arg = null);
--- a/dom/bindings/test/TestExampleGen.webidl +++ b/dom/bindings/test/TestExampleGen.webidl @@ -302,16 +302,17 @@ interface TestExampleInterface { void passFloat64Array(Float64Array arg); void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg); void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg); void passMozMapOfArrayBuffers(MozMap<ArrayBuffer> arg); void passMozMapOfNullableArrayBuffers(MozMap<ArrayBuffer?> arg); void passVariadicTypedArray(Float32Array... arg); void passVariadicNullableTypedArray(Float32Array?... arg); Uint8Array receiveUint8Array(); + attribute Uint8Array uint8ArrayAttr; // DOMString types void passString(DOMString arg); void passNullableString(DOMString? arg); void passOptionalString(optional DOMString arg); void passOptionalStringWithDefaultValue(optional DOMString arg = "abc"); void passOptionalNullableString(optional DOMString? arg); void passOptionalNullableStringWithDefaultValue(optional DOMString? arg = null);
--- a/dom/bindings/test/TestJSImplGen.webidl +++ b/dom/bindings/test/TestJSImplGen.webidl @@ -298,43 +298,40 @@ interface TestJSImplInterface { MozMap<long> receiveMozMap(); MozMap<long>? receiveNullableMozMap(); MozMap<long?> receiveMozMapOfNullableInts(); MozMap<long?>? receiveNullableMozMapOfNullableInts(); //XXXbz No support for MozMap of MozMaps return values yet. //MozMap<MozMap<long>> receiveMozMapOfMozMaps(); MozMap<any> receiveAnyMozMap(); - // ArrayBuffer is handled differently in callback interfaces and the example generator. - // Need to figure out what should be done there. Seems like other typed array stuff is - // similarly not working in the JS implemented generator. Probably some other issues - // here as well. // Typed array types - //void passArrayBuffer(ArrayBuffer arg); - //void passNullableArrayBuffer(ArrayBuffer? arg); - //void passOptionalArrayBuffer(optional ArrayBuffer arg); - //void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg); - //void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg= null); - //void passArrayBufferView(ArrayBufferView arg); - //void passInt8Array(Int8Array arg); - //void passInt16Array(Int16Array arg); - //void passInt32Array(Int32Array arg); - //void passUint8Array(Uint8Array arg); - //void passUint16Array(Uint16Array arg); - //void passUint32Array(Uint32Array arg); - //void passUint8ClampedArray(Uint8ClampedArray arg); - //void passFloat32Array(Float32Array arg); - //void passFloat64Array(Float64Array arg); - //void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg); - //void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg); - //void passMozMapOfArrayBuffers(MozMap<ArrayBuffer> arg); - //void passMozMapOfNullableArrayBuffers(MozMap<ArrayBuffer?> arg); - //void passVariadicTypedArray(Float32Array... arg); - //void passVariadicNullableTypedArray(Float32Array?... arg); - //Uint8Array receiveUint8Array(); + void passArrayBuffer(ArrayBuffer arg); + void passNullableArrayBuffer(ArrayBuffer? arg); + void passOptionalArrayBuffer(optional ArrayBuffer arg); + void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg); + void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg= null); + void passArrayBufferView(ArrayBufferView arg); + void passInt8Array(Int8Array arg); + void passInt16Array(Int16Array arg); + void passInt32Array(Int32Array arg); + void passUint8Array(Uint8Array arg); + void passUint16Array(Uint16Array arg); + void passUint32Array(Uint32Array arg); + void passUint8ClampedArray(Uint8ClampedArray arg); + void passFloat32Array(Float32Array arg); + void passFloat64Array(Float64Array arg); + void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg); + void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg); + void passMozMapOfArrayBuffers(MozMap<ArrayBuffer> arg); + void passMozMapOfNullableArrayBuffers(MozMap<ArrayBuffer?> arg); + void passVariadicTypedArray(Float32Array... arg); + void passVariadicNullableTypedArray(Float32Array?... arg); + Uint8Array receiveUint8Array(); + attribute Uint8Array uint8ArrayAttr; // DOMString types void passString(DOMString arg); void passNullableString(DOMString? arg); void passOptionalString(optional DOMString arg); void passOptionalStringWithDefaultValue(optional DOMString arg = "abc"); void passOptionalNullableString(optional DOMString? arg); void passOptionalNullableStringWithDefaultValue(optional DOMString? arg = null);
--- a/dom/browser-element/BrowserElementChildPreload.js +++ b/dom/browser-element/BrowserElementChildPreload.js @@ -76,27 +76,31 @@ let SEC_ERROR_UNTRUSTED_ISSUER = (SEC_ER let SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = (SEC_ERROR_BASE + 30); let SEC_ERROR_UNTRUSTED_CERT = (SEC_ERROR_BASE + 21); let SEC_ERROR_EXPIRED_CERTIFICATE = (SEC_ERROR_BASE + 11); let SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = (SEC_ERROR_BASE + 176); let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; let SSL_ERROR_BAD_CERT_DOMAIN = (SSL_ERROR_BASE + 12); +let MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE; +let MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = (MOZILLA_PKIX_ERROR_BASE + 1); + function getErrorClass(errorCode) { let NSPRCode = -1 * NS_ERROR_GET_CODE(errorCode); switch (NSPRCode) { case SEC_ERROR_UNKNOWN_ISSUER: case SEC_ERROR_UNTRUSTED_ISSUER: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: case SEC_ERROR_UNTRUSTED_CERT: case SSL_ERROR_BAD_CERT_DOMAIN: case SEC_ERROR_EXPIRED_CERTIFICATE: case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: + case MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: return Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT; default: return Ci.nsINSSErrorsService.ERROR_CLASS_SSL_PROTOCOL; } return null; }
--- a/dom/canvas/CanvasImageCache.cpp +++ b/dom/canvas/CanvasImageCache.cpp @@ -80,55 +80,130 @@ public: enum { ALLOW_MEMMOVE = true }; nsAutoPtr<ImageCacheEntryData> mData; }; static bool sPrefsInitialized = false; static int32_t sCanvasImageCacheLimit = 0; +class ImageCacheObserver; + class ImageCache MOZ_FINAL : public nsExpirationTracker<ImageCacheEntryData,4> { public: // We use 3 generations of 1 second each to get a 2-3 seconds timeout. enum { GENERATION_MS = 1000 }; - ImageCache() - : nsExpirationTracker<ImageCacheEntryData,4>(GENERATION_MS) - , mTotal(0) - { - if (!sPrefsInitialized) { - sPrefsInitialized = true; - Preferences::AddIntVarCache(&sCanvasImageCacheLimit, "canvas.image.cache.limit", 0); - } - } - ~ImageCache() { - AgeAllGenerations(); - } + ImageCache(); + ~ImageCache(); virtual void NotifyExpired(ImageCacheEntryData* aObject) { mTotal -= aObject->SizeInBytes(); RemoveObject(aObject); // Deleting the entry will delete aObject since the entry owns aObject mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas)); } nsTHashtable<ImageCacheEntry> mCache; size_t mTotal; + nsRefPtr<ImageCacheObserver> mImageCacheObserver; }; static ImageCache* gImageCache = nullptr; +// Listen memory-pressure event for image cache purge +class ImageCacheObserver MOZ_FINAL : public nsIObserver +{ +public: + NS_DECL_ISUPPORTS + + ImageCacheObserver(ImageCache* aImageCache) + : mImageCache(aImageCache) + { + RegisterMemoryPressureEvent(); + } + + void Destroy() + { + UnregisterMemoryPressureEvent(); + mImageCache = nullptr; + } + + NS_IMETHODIMP Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aSomeData) + { + if (!mImageCache || strcmp(aTopic, "memory-pressure")) { + return NS_OK; + } + + mImageCache->AgeAllGenerations(); + return NS_OK; + } + +private: + virtual ~ImageCacheObserver() + { + } + + void RegisterMemoryPressureEvent() + { + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + MOZ_ASSERT(observerService); + + if (observerService) { + observerService->AddObserver(this, "memory-pressure", false); + } + } + + void UnregisterMemoryPressureEvent() + { + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + // Do not assert on observerService here. This might be triggered by + // the cycle collector at a late enough time, that XPCOM services are + // no longer available. See bug 1029504. + if (observerService) { + observerService->RemoveObserver(this, "memory-pressure"); + } + } + + ImageCache* mImageCache; +}; + +NS_IMPL_ISUPPORTS(ImageCacheObserver, nsIObserver) + class CanvasImageCacheShutdownObserver MOZ_FINAL : public nsIObserver { ~CanvasImageCacheShutdownObserver() {} public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER }; +ImageCache::ImageCache() + : nsExpirationTracker<ImageCacheEntryData,4>(GENERATION_MS) + , mTotal(0) +{ + if (!sPrefsInitialized) { + sPrefsInitialized = true; + Preferences::AddIntVarCache(&sCanvasImageCacheLimit, "canvas.image.cache.limit", 0); + } + mImageCacheObserver = new ImageCacheObserver(this); + MOZ_RELEASE_ASSERT(mImageCacheObserver, "Can't alloc ImageCacheObserver"); +} + +ImageCache::~ImageCache() { + AgeAllGenerations(); + mImageCacheObserver->Destroy(); +} + void CanvasImageCache::NotifyDrawImage(Element* aImage, HTMLCanvasElement* aCanvas, imgIRequest* aRequest, SourceSurface* aSource, const gfxIntSize& aSize) { if (!gImageCache) {
--- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -86,16 +86,17 @@ #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include "mozilla/unused.h" #include "nsCCUncollectableMarker.h" #include "nsWrapperCacheInlines.h" #include "mozilla/dom/CanvasRenderingContext2DBinding.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLVideoElement.h" +#include "mozilla/dom/SVGMatrix.h" #include "mozilla/dom/TextMetrics.h" #include "mozilla/dom/UnionTypes.h" #include "mozilla/dom/SVGMatrix.h" #include "nsGlobalWindow.h" #include "GLContext.h" #include "GLContextProvider.h" #include "SVGContentUtils.h" #include "SVGImageContext.h" @@ -4589,16 +4590,35 @@ CanvasPath::BezierTo(const gfx::Point& a const gfx::Point& aCP2, const gfx::Point& aCP3) { EnsurePathBuilder(); mPathBuilder->BezierTo(aCP1, aCP2, aCP3); } +void +CanvasPath::AddPath(CanvasPath& aCanvasPath, const Optional<NonNull<SVGMatrix>>& aMatrix) +{ + RefPtr<gfx::Path> tempPath = aCanvasPath.GetPath(CanvasWindingRule::Nonzero, + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()); + + if (aMatrix.WasPassed()) { + const SVGMatrix& m = aMatrix.Value(); + Matrix transform(m.A(), m.B(), m.C(), m.D(), m.E(), m.F()); + + if (!transform.IsIdentity()) { + RefPtr<PathBuilder> tempBuilder = tempPath->TransformedCopyToBuilder(transform, FillRule::FILL_WINDING); + tempPath = tempBuilder->Finish(); + } + } + + tempPath->StreamToSink(mPathBuilder); +} + TemporaryRef<gfx::Path> CanvasPath::GetPath(const CanvasWindingRule& winding, const DrawTarget* aTarget) const { FillRule fillRule = FillRule::FILL_WINDING; if (winding == CanvasWindingRule::Evenodd) { fillRule = FillRule::FILL_EVEN_ODD; }
--- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -36,16 +36,17 @@ class SurfaceStream; } namespace dom { class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement; class ImageData; class StringOrCanvasGradientOrCanvasPattern; class OwningStringOrCanvasGradientOrCanvasPattern; class TextMetrics; +class SVGMatrix; extern const mozilla::gfx::Float SIGMA_MAX; template<typename T> class Optional; class CanvasPath MOZ_FINAL : public nsWrapperCache { @@ -89,16 +90,19 @@ public: const gfx::DrawTarget* aTarget) const; explicit CanvasPath(nsISupports* aParent); // TemporaryRef arg because the return value from Path::CopyToBuilder() is // passed directly and we can't drop the only ref to have a raw pointer. CanvasPath(nsISupports* aParent, TemporaryRef<gfx::PathBuilder> aPathBuilder); + void AddPath(CanvasPath& aCanvasPath, + const Optional<NonNull<SVGMatrix>>& aMatrix); + private: virtual ~CanvasPath() {} nsCOMPtr<nsISupports> mParent; static gfx::Float ToFloat(double aValue) { return gfx::Float(aValue); } mutable RefPtr<gfx::Path> mPath; mutable RefPtr<gfx::PathBuilder> mPathBuilder;
--- a/dom/canvas/test/test_canvas_path.html +++ b/dom/canvas/test/test_canvas_path.html @@ -332,60 +332,110 @@ function test_pathconstructor_canvas() { // copy constructor. This should not crash. var p1 = new Path2D(); var _p2 = new Path2D(p1); p1.arcTo(0, 0, 1, 1, 2); } </script> +<p>Canvas test: test_addpath_canvas</p> +<canvas id="c8" class="output" width="200" height="200">+ +</canvas> +<script type="text/javascript"> + +function test_addpath_canvas() { + var c = document.getElementById("c8"); + var ctx = c.getContext("2d"); + ctx.beginPath(); + var p1 = new Path2D(); + p1.rect(0,0,100,100); + var p2 = new Path2D(); + p2.rect(0,100,100,100); + var m = ctx.currentTransform; + p1.addPath(p2, m); + ctx.fillStyle = 'yellow'; + ctx.fill(p1); + isPixel(ctx, 0, 100, [255, 255, 0, 255], 0); + + ctx.clearRect(0,0,200,200); + + ctx.beginPath(); + var p3 = new Path2D(); + p3.rect(0,0,100,100); + var p4 = new Path2D(); + p4.rect(0,100,100,100); + var m = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix(); + m.a = 1; m.b = 0; + m.c = 0; m.d = 1; + m.e = 100; m.f = -100; + p3.addPath(p4, m); + ctx.fillStyle = 'yellow'; + ctx.fill(p3); + isPixel(ctx, 50, 50, [255, 255, 0, 255], 0); + isPixel(ctx, 150, 150, [0, 0, 0, 0], 0); + + var p5 = new Path2D(); + p5.rect(0,0,100,100); + shouldThrow(ctx, "p5.addPath(null, m)"); + shouldThrow(ctx, "p5.addPath([], m)"); + shouldThrow(ctx, "p5.addPath({}, m)"); +} +</script> + <script> function runTests() { try { test_drawClipPath_canvas(); } catch(e) { + ok(false, "unexpected exception thrown in: test_drawClipPath_canvas"); throw e; - ok(false, "unexpected exception thrown in: test_drawClipPath_canvas"); } try { test_drawFillPath_canvas(); } catch(e) { + ok(false, "unexpected exception thrown in: test_drawFillPath_canvas"); throw e; - ok(false, "unexpected exception thrown in: test_drawFillPath_canvas"); } try { test_drawStrokePath_canvas(); } catch(e) { + ok(false, "unexpected exception thrown in: test_drawStrokePath_canvas"); throw e; - ok(false, "unexpected exception thrown in: test_drawStrokePath_canvas"); } try { test_large_canvas(); } catch(e) { + ok(false, "unexpected exception thrown in: test_large_canvas"); throw e; - ok(false, "unexpected exception thrown in: test_large_canvas"); } try { test_isPointInPath_canvas(); } catch(e) { + ok(false, "unexpected exception thrown in: test_isPointInPath_canvas"); throw e; - ok(false, "unexpected exception thrown in: test_isPointInPath_canvas"); } try { test_isPointInStroke_canvas(); } catch(e) { + ok(false, "unexpected exception thrown in: test_isPointInStroke_canvas"); throw e; - ok(false, "unexpected exception thrown in: test_isPointInStroke_canvas"); } try { test_pathconstructor_canvas(); } catch(e) { + ok(false, "unexpected exception thrown in: test_pathconstructor_canvas"); throw e; - ok(false, "unexpected exception thrown in: test_pathconstructor_canvas"); + } + try { + test_addpath_canvas(); + } catch(e) { + ok(false, "unexpected exception thrown in: test_addpath_canvas"); + throw e; } SpecialPowers.setBoolPref("canvas.path.enabled", false); SimpleTest.finish(); } addLoadEvent(runTests); // Don't leak the world via the Path2D reference to its window.
--- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -1591,18 +1591,18 @@ EventStateManager::GenerateDragGesture(n if (!pixelThresholdY) pixelThresholdY = 5; } // fire drag gesture if mouse has moved enough LayoutDeviceIntPoint pt = aEvent->refPoint + LayoutDeviceIntPoint::FromUntyped(aEvent->widget->WidgetToScreenOffset()); LayoutDeviceIntPoint distance = pt - mGestureDownPoint; - if (Abs(distance.x.value) > SafeCast<uint32_t>(pixelThresholdX) || - Abs(distance.y.value) > SafeCast<uint32_t>(pixelThresholdY)) { + if (Abs(distance.x.value) > AssertedCast<uint32_t>(pixelThresholdX) || + Abs(distance.y.value) > AssertedCast<uint32_t>(pixelThresholdY)) { if (Prefs::ClickHoldContextMenu()) { // stop the click-hold before we fire off the drag gesture, in case // it takes a long time KillClickHoldTimer(); } nsCOMPtr<nsISupports> container = aPresContext->GetContainerWeak(); nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(container);
--- a/dom/webidl/CanvasRenderingContext2D.webidl +++ b/dom/webidl/CanvasRenderingContext2D.webidl @@ -318,10 +318,12 @@ interface TextMetrics { }; [Pref="canvas.path.enabled", Constructor, Constructor(Path2D other), Constructor(DOMString pathString)] interface Path2D -{}; +{ + void addPath(Path2D path, optional SVGMatrix transformation); +}; Path2D implements CanvasPathMethods;
--- a/editor/libeditor/nsEditor.cpp +++ b/editor/libeditor/nsEditor.cpp @@ -1684,24 +1684,25 @@ nsEditor::InsertContainerAbove(nsIConten /////////////////////////////////////////////////////////////////////////// // MoveNode: move aNode to {aParent,aOffset} nsresult nsEditor::MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset) { MOZ_ASSERT(aNode); MOZ_ASSERT(aParent); MOZ_ASSERT(aOffset == -1 || - (0 <= aOffset && SafeCast<uint32_t>(aOffset) <= aParent->Length())); + (0 <= aOffset && + AssertedCast<uint32_t>(aOffset) <= aParent->Length())); nsCOMPtr<nsINode> oldParent = aNode->GetParentNode(); int32_t oldOffset = oldParent ? oldParent->IndexOf(aNode) : -1; if (aOffset == -1) { // Magic value meaning "move to end of aParent" - aOffset = SafeCast<int32_t>(aParent->Length()); + aOffset = AssertedCast<int32_t>(aParent->Length()); } // Don't do anything if it's already in right place if (aParent == oldParent && aOffset == oldOffset) { return NS_OK; } // Notify our internal selection state listener @@ -2865,17 +2866,17 @@ nsEditor::JoinNodesImpl(nsINode* aNodeTo } // delete the extra node ErrorResult err; aParent->RemoveChild(*aNodeToJoin, err); if (GetShouldTxnSetSelection()) { // editor wants us to set selection at join point - selection->Collapse(aNodeToKeep, SafeCast<int32_t>(firstNodeLength)); + selection->Collapse(aNodeToKeep, AssertedCast<int32_t>(firstNodeLength)); } else if (selStartNode) { // and adjust the selection if needed // HACK: this is overly simplified - multi-range selections need more work than this bool bNeedToAdjust = false; // check to see if we joined nodes where selection starts if (selStartNode == aNodeToJoin) { bNeedToAdjust = true;
--- a/editor/libeditor/nsWSRunObject.cpp +++ b/editor/libeditor/nsWSRunObject.cpp @@ -1353,23 +1353,25 @@ nsWSRunObject::DeleteChars(nsINode* aSta nsRefPtr<Text> node = mNodeArray[idx]; if (!node) { // We ran out of ws nodes; must have been deleting to end return NS_OK; } if (node == aStartNode) { uint32_t len = node->Length(); if (uint32_t(aStartOffset) < len) { - res = mHTMLEditor->DeleteText(*node, SafeCast<uint32_t>(aStartOffset), + res = mHTMLEditor->DeleteText(*node, + AssertedCast<uint32_t>(aStartOffset), len - aStartOffset); NS_ENSURE_SUCCESS(res, res); } } else if (node == aEndNode) { if (aEndOffset) { - res = mHTMLEditor->DeleteText(*node, 0, SafeCast<uint32_t>(aEndOffset)); + res = mHTMLEditor->DeleteText(*node, 0, + AssertedCast<uint32_t>(aEndOffset)); NS_ENSURE_SUCCESS(res, res); } break; } else { if (!range) { range = new nsRange(aStartNode); res = range->Set(aStartNode, aStartOffset, aEndNode, aEndOffset); NS_ENSURE_SUCCESS(res, res);
--- a/extensions/spellcheck/hunspell/src/mozHunspellAllocator.h +++ b/extensions/spellcheck/hunspell/src/mozHunspellAllocator.h @@ -2,15 +2,15 @@ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozHunspellAllocator_h__ #define mozHunspellAllocator_h__ -#include "nsIMemoryReporter.h" +#include "mozilla/CountingAllocatorBase.h" class HunspellAllocator : public mozilla::CountingAllocatorBase<HunspellAllocator> { }; #endif
--- a/gfx/2d/HelpersSkia.h +++ b/gfx/2d/HelpersSkia.h @@ -14,51 +14,16 @@ #include "skia/GrTypes.h" #endif #include "mozilla/Assertions.h" #include <vector> namespace mozilla { namespace gfx { -static inline SkBitmap::Config -GfxFormatToSkiaConfig(SurfaceFormat format) -{ - switch (format) - { - case SurfaceFormat::B8G8R8A8: - return SkBitmap::kARGB_8888_Config; - case SurfaceFormat::B8G8R8X8: - // We probably need to do something here. - return SkBitmap::kARGB_8888_Config; - case SurfaceFormat::R5G6B5: - return SkBitmap::kRGB_565_Config; - case SurfaceFormat::A8: - return SkBitmap::kA8_Config; - default: - return SkBitmap::kARGB_8888_Config; - } -} - -static inline SurfaceFormat -SkiaConfigToGfxFormat(SkBitmap::Config config) -{ - switch (config) - { - case SkBitmap::kARGB_8888_Config: - return SurfaceFormat::B8G8R8A8; - case SkBitmap::kRGB_565_Config: - return SurfaceFormat::R5G6B5; - case SkBitmap::kA8_Config: - return SurfaceFormat::A8; - default: - return SurfaceFormat::B8G8R8A8; - } -} - static inline SkColorType GfxFormatToSkiaColorType(SurfaceFormat format) { switch (format) { case SurfaceFormat::B8G8R8A8: return kBGRA_8888_SkColorType; case SurfaceFormat::B8G8R8X8:
--- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -17,16 +17,17 @@ #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for Point, IntPoint #include "mozilla/gfx/Rect.h" // for IntRect, Rect #include "mozilla/layers/Compositor.h" // for Compositor, etc #include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::CONTAINER #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc #include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget #include "mozilla/layers/AsyncPanZoomController.h" // for AsyncPanZoomController +#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform #include "mozilla/mozalloc.h" // for operator delete, etc #include "nsAutoPtr.h" // for nsRefPtr #include "nsDebug.h" // for NS_ASSERTION #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE #include "nsPoint.h" // for nsIntPoint #include "nsRect.h" // for nsIntRect #include "nsRegion.h" // for nsIntRegion @@ -81,26 +82,16 @@ GetOpaqueRect(Layer* aLayer) const nsIntRect* clipRect = aLayer->GetEffectiveClipRect(); if (clipRect) { result.IntersectRect(result, *clipRect); } return result; } -static gfx::Point GetScrollData(Layer* aLayer) { - gfx::Matrix matrix; - if (aLayer->GetLocalTransform().Is2D(&matrix)) { - return matrix.GetTranslation(); - } - - gfx::Point origin; - return origin; -} - static void DrawLayerInfo(const RenderTargetIntRect& aClipRect, LayerManagerComposite* aManager, Layer* aLayer) { if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) { // XXX - should figure out a way to render this, but for now this // is hard to do, since it will often get superimposed over the first @@ -114,38 +105,41 @@ static void DrawLayerInfo(const RenderTa nsIntRegion visibleRegion = aLayer->GetVisibleRegion(); uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().width, 500); nsIntPoint topLeft = visibleRegion.GetBounds().TopLeft(); aManager->GetTextRenderer()->RenderText(ss.str().c_str(), gfx::IntPoint(topLeft.x, topLeft.y), aLayer->GetEffectiveTransform(), 16, maxWidth); - } static void PrintUniformityInfo(Layer* aLayer) { // Don't want to print a log for smaller layers if (aLayer->GetEffectiveVisibleRegion().GetBounds().width < 300 || aLayer->GetEffectiveVisibleRegion().GetBounds().height < 300) { return; } FrameMetrics frameMetrics = aLayer->GetFrameMetrics(); if (!frameMetrics.IsScrollable()) { return; } - const LayerPoint scrollOffset = frameMetrics.GetScrollOffsetInLayerPixels(); - const gfx::Point layerTransform = GetScrollData(aLayer); - const gfx::Point layerScroll = scrollOffset.ToUnknownPoint() - layerTransform; - - printf_stderr("UniformityInfo Layer_Move %llu %p %f, %f\n", - TimeStamp::Now(), aLayer, layerScroll.x, layerScroll.y); + AsyncPanZoomController* apzc = aLayer->GetAsyncPanZoomController(); + if (apzc) { + ViewTransform asyncTransform, overscrollTransform; + ScreenPoint scrollOffset; + apzc->SampleContentTransformForFrame(&asyncTransform, + scrollOffset, + &overscrollTransform); + printf_stderr("UniformityInfo Layer_Move %llu %p %f, %f\n", + TimeStamp::Now(), aLayer, scrollOffset.x.value, scrollOffset.y.value); + } } /* all of the per-layer prepared data we need to maintain */ struct PreparedLayer { PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect, bool aRestoreVisibleRegion, nsIntRegion &aVisibleRegion) : mLayer(aLayer), mClipRect(aClipRect), mRestoreVisibleRegion(aRestoreVisibleRegion), mSavedVisibleRegion(aVisibleRegion) {} LayerComposite* mLayer;
--- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -406,61 +406,97 @@ LayerManagerComposite::RenderDebugOverla if (drawFrameColorBars || drawFrameCounter) { // We intentionally overflow at 2^16. sFrameCount++; } } RefPtr<CompositingRenderTarget> -LayerManagerComposite::PushGroup() +LayerManagerComposite::PushGroupForLayerEffects() { + // This is currently true, so just making sure that any new use of this + // method is flagged for investigation + MOZ_ASSERT(gfxPrefs::LayersEffectInvert() || + gfxPrefs::LayersEffectGrayscale() || + gfxPrefs::LayersEffectContrast() != 0.0); + RefPtr<CompositingRenderTarget> previousTarget = mCompositor->GetCurrentRenderTarget(); // make our render target the same size as the destination target // so that we don't have to change size if the drawing area changes. IntRect rect(previousTarget->GetOrigin(), previousTarget->GetSize()); // XXX: I'm not sure if this is true or not... MOZ_ASSERT(rect.x == 0 && rect.y == 0); if (!mTwoPassTmpTarget || mTwoPassTmpTarget->GetSize() != previousTarget->GetSize() || mTwoPassTmpTarget->GetOrigin() != previousTarget->GetOrigin()) { mTwoPassTmpTarget = mCompositor->CreateRenderTarget(rect, INIT_MODE_NONE); } mCompositor->SetRenderTarget(mTwoPassTmpTarget); return previousTarget; } -void LayerManagerComposite::PopGroup(RefPtr<CompositingRenderTarget> aPreviousTarget, nsIntRect aClipRect) +void +LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget, + nsIntRect aClipRect, + bool aGrayscaleEffect, + bool aInvertEffect, + float aContrastEffect) { + MOZ_ASSERT(mTwoPassTmpTarget); + + // This is currently true, so just making sure that any new use of this + // method is flagged for investigation + MOZ_ASSERT(aInvertEffect || aGrayscaleEffect || aContrastEffect != 0.0); + mCompositor->SetRenderTarget(aPreviousTarget); EffectChain effectChain(RootLayer()); - Matrix5x4 matrix; - if (gfxPrefs::Grayscale()) { - matrix._11 = matrix._12 = matrix._13 = 0.2126f; - matrix._21 = matrix._22 = matrix._23 = 0.7152f; - matrix._31 = matrix._32 = matrix._33 = 0.0722f; + Matrix5x4 effectMatrix; + if (aGrayscaleEffect) { + // R' = G' = B' = luminance + // R' = 0.2126*R + 0.7152*G + 0.0722*B + // G' = 0.2126*R + 0.7152*G + 0.0722*B + // B' = 0.2126*R + 0.7152*G + 0.0722*B + Matrix5x4 grayscaleMatrix(0.2126f, 0.2126f, 0.2126f, 0, + 0.7152f, 0.7152f, 0.7152f, 0, + 0.0722f, 0.0722f, 0.0722f, 0, + 0, 0, 0, 1, + 0, 0, 0, 0); + effectMatrix = grayscaleMatrix; } - if (gfxPrefs::Invert()) { - matrix._11 = -matrix._11; - matrix._12 = -matrix._12; - matrix._13 = -matrix._13; - matrix._21 = -matrix._21; - matrix._22 = -matrix._22; - matrix._23 = -matrix._23; - matrix._31 = -matrix._31; - matrix._32 = -matrix._32; - matrix._33 = -matrix._33; - matrix._51 = 1; - matrix._52 = 1; - matrix._53 = 1; + if (aInvertEffect) { + // R' = 1 - R + // G' = 1 - G + // B' = 1 - B + Matrix5x4 colorInvertMatrix(-1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1, + 1, 1, 1, 0); + effectMatrix = effectMatrix * colorInvertMatrix; + } + + if (aContrastEffect != 0.0) { + // Multiplying with: + // R' = (1 + c) * (R - 0.5) + 0.5 + // G' = (1 + c) * (G - 0.5) + 0.5 + // B' = (1 + c) * (B - 0.5) + 0.5 + float cP1 = aContrastEffect + 1; + float hc = 0.5*aContrastEffect; + Matrix5x4 contrastMatrix( cP1, 0, 0, 0, + 0, cP1, 0, 0, + 0, 0, cP1, 0, + 0, 0, 0, 1, + -hc, -hc, -hc, 0); + effectMatrix = effectMatrix * contrastMatrix; } effectChain.mPrimaryEffect = new EffectRenderTarget(mTwoPassTmpTarget); - effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(matrix); + effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(effectMatrix); gfx::Rect clipRectF(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height); mCompositor->DrawQuad(Rect(Point(0, 0), Size(mTwoPassTmpTarget->GetSize())), clipRectF, effectChain, 1., Matrix4x4()); } void LayerManagerComposite::Render() @@ -468,18 +504,25 @@ LayerManagerComposite::Render() PROFILER_LABEL("LayerManagerComposite", "Render", js::ProfileEntry::Category::GRAPHICS); if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } - /** Our more efficient but less powerful alter ego, if one is available. */ - nsRefPtr<Composer2D> composer2D = mCompositor->GetWidget()->GetComposer2D(); + // At this time, it doesn't really matter if these preferences change + // during the execution of the function; we should be safe in all + // permutations. However, may as well just get the values onces and + // then use them, just in case the consistency becomes important in + // the future. + bool invertVal = gfxPrefs::LayersEffectInvert(); + bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); + float contrastVal = gfxPrefs::LayersEffectContrast(); + bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // Dump to console if (gfxPrefs::LayersDump()) { this->Dump(); } @@ -489,18 +532,23 @@ LayerManagerComposite::Render() // Create a LayersPacket, dump Layers into it and transfer the // packet('s ownership) to LayerScope. auto packet = MakeUnique<layerscope::Packet>(); layerscope::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(Move(packet)); } - if (gfxPrefs::Invert() || gfxPrefs::Grayscale()) { - composer2D = nullptr; + /** Our more efficient but less powerful alter ego, if one is available. */ + nsRefPtr<Composer2D> composer2D; + + // We can't use composert2D if we have layer effects, so only get it + // when we don't have any effects. + if (!haveLayerEffects) { + composer2D = mCompositor->GetWidget()->GetComposer2D(); } if (!mTarget && composer2D && composer2D->TryRender(mRoot, mWorldMatrix, mGeometryChanged)) { if (mFPS) { double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now()); if (gfxPrefs::LayersDrawFPS()) { printf_stderr("HWComposer: FPS is %g\n", fps); } @@ -553,18 +601,18 @@ LayerManagerComposite::Render() // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay(this, nsIntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height)); RefPtr<CompositingRenderTarget> previousTarget; - if (gfxPrefs::Invert() || gfxPrefs::Grayscale()) { - previousTarget = PushGroup(); + if (haveLayerEffects) { + previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Render our layers. RootLayer()->Prepare(RenderTargetPixel::FromUntyped(clipRect)); RootLayer()->RenderLayer(clipRect); @@ -572,17 +620,19 @@ LayerManagerComposite::Render() nsIntRegionRectIterator iter(mRegionToClear); const nsIntRect *r; while ((r = iter.Next())) { mCompositor->ClearRect(Rect(r->x, r->y, r->width, r->height)); } } if (mTwoPassTmpTarget) { - PopGroup(previousTarget, clipRect); + MOZ_ASSERT(haveLayerEffects); + PopGroupForLayerEffects(previousTarget, clipRect, + grayscaleVal, invertVal, contrastVal); } // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay(this, nsIntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height));
--- a/gfx/layers/composite/LayerManagerComposite.h +++ b/gfx/layers/composite/LayerManagerComposite.h @@ -264,18 +264,22 @@ private: /** * Render debug overlays such as the FPS/FrameCounter above the frame. */ void RenderDebugOverlay(const gfx::Rect& aBounds); void WorldTransformRect(nsIntRect& aRect); - RefPtr<CompositingRenderTarget> PushGroup(); - void PopGroup(RefPtr<CompositingRenderTarget> aPreviousTarget, nsIntRect aClipRect); + RefPtr<CompositingRenderTarget> PushGroupForLayerEffects(); + void PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget, + nsIntRect aClipRect, + bool aGrayscaleEffect, + bool aInvertEffect, + float aContrastEffect); RefPtr<Compositor> mCompositor; nsAutoPtr<LayerProperties> mClonedLayerTreeProperties; /** * Context target, nullptr when drawing directly to our swap chain. */ RefPtr<gfx::DrawTarget> mTarget;
--- a/gfx/skia/trunk/include/config/SkUserConfig.h +++ b/gfx/skia/trunk/include/config/SkUserConfig.h @@ -201,12 +201,11 @@ #else # define SK_BARRIERS_PLATFORM_H "skia/SkBarriers_x86.h" #endif #define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0 #define SK_SUPPORT_LEGACY_GETDEVICE #define SK_SUPPORT_LEGACY_GETTOPDEVICE -#define SK_SUPPORT_LEGACY_BITMAP_CONFIG #define SK_IGNORE_ETC1_SUPPORT #endif
--- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -3,16 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "base/basictypes.h" #include "gfxAndroidPlatform.h" #include "mozilla/gfx/2D.h" +#include "mozilla/CountingAllocatorBase.h" #include "mozilla/Preferences.h" #include "gfx2DGlue.h" #include "gfxFT2FontList.h" #include "gfxImageSurface.h" #include "mozilla/dom/ContentChild.h" #include "nsXULAppAPI.h" #include "nsIScreen.h"
--- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -217,16 +217,23 @@ private: DECL_GFX_PREF(Once, "layers.componentalpha.enabled", ComponentAlphaEnabled, bool, true); #endif DECL_GFX_PREF(Live, "layers.draw-bigimage-borders", DrawBigImageBorders, bool, false); DECL_GFX_PREF(Live, "layers.draw-borders", DrawLayerBorders, bool, false); DECL_GFX_PREF(Live, "layers.draw-tile-borders", DrawTileBorders, bool, false); DECL_GFX_PREF(Live, "layers.flash-borders", FlashLayerBorders, bool, false); DECL_GFX_PREF(Live, "layers.draw-layer-info", DrawLayerInfo, bool, false); DECL_GFX_PREF(Live, "layers.dump", LayersDump, bool, false); + + // 0 is "no change" for contrast, positive values increase it, negative values + // decrease it until we hit mid gray at -1 contrast, after that it gets weird. + DECL_GFX_PREF(Live, "layers.effect.contrast", LayersEffectContrast, float, 0.0f); + DECL_GFX_PREF(Live, "layers.effect.grayscale", LayersEffectGrayscale, bool, false); + DECL_GFX_PREF(Live, "layers.effect.invert", LayersEffectInvert, bool, false); + DECL_GFX_PREF(Once, "layers.enable-tiles", LayersTilesEnabled, bool, false); DECL_GFX_PREF(Once, "layers.simple-tiles", LayersUseSimpleTiles, bool, false); DECL_GFX_PREF(Once, "layers.force-per-tile-drawing", PerTileDrawing, bool, false); DECL_GFX_PREF(Once, "layers.tiled-drawtarget.enabled", TiledDrawTargetEnabled, bool, false); // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose // screen size does not align nicely to the default tile size. Although layers can be any size, // they are often the same size as the screen, especially for width. DECL_GFX_PREF(Once, "layers.tile-width", LayersTileWidth, int32_t, 256); @@ -244,18 +251,16 @@ private: DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-enabled", LayersOffMainThreadCompositionForceEnabled, bool, false); DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.testing.enabled", LayersOffMainThreadCompositionTestingEnabled, bool, false); DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces", UseImageOffscreenSurfaces, bool, false); DECL_GFX_PREF(Live, "layers.orientation.sync.timeout", OrientationSyncMillis, uint32_t, (uint32_t)0); DECL_GFX_PREF(Once, "layers.prefer-d3d9", LayersPreferD3D9, bool, false); DECL_GFX_PREF(Once, "layers.prefer-opengl", LayersPreferOpenGL, bool, false); DECL_GFX_PREF(Once, "layers.progressive-paint", UseProgressiveTilePainting, bool, false); DECL_GFX_PREF(Once, "layers.uniformity-info", UniformityInfo, bool, false); - DECL_GFX_PREF(Live, "layers.invert", Invert, bool, false); - DECL_GFX_PREF(Live, "layers.grayscale", Grayscale, bool, false); DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f); DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled", ScrollBehaviorEnabled, bool, false); DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f); DECL_GFX_PREF(Once, "layout.css.touch_action.enabled", TouchActionEnabled, bool, false); DECL_GFX_PREF(Once, "layout.frame_rate", LayoutFrameRate, int32_t, -1); DECL_GFX_PREF(Live, "layout.display-list.dump", LayoutDumpDisplayList, bool, false); DECL_GFX_PREF(Once, "layout.paint_rects_separately", LayoutPaintRectsSeparately, bool, true);
--- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -5960,19 +5960,21 @@ GenerateEntry(ModuleCompiler &m, unsigne // In constrast to the system ABI, the Ion convention is that all registers // are clobbered by calls. Thus, we must save the caller's non-volatile // registers. masm.PushRegsInMask(NonVolatileRegs); JS_ASSERT(masm.framePushed() == FramePushedAfterSave); // ARM and MIPS have a globally-pinned GlobalReg (x64 uses RIP-relative - // addressing, x86 uses immediates in effective addresses). + // addressing, x86 uses immediates in effective addresses). For the + // AsmJSGlobalRegBias addition, see Assembler-(mips,arm).h. #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) masm.movePtr(IntArgReg1, GlobalReg); + masm.addPtr(Imm32(AsmJSGlobalRegBias), GlobalReg); #endif // ARM, MIPS and x64 have a globally-pinned HeapReg (x86 uses immediates in // effective addresses). #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) masm.loadPtr(Address(IntArgReg1, AsmJSModule::heapGlobalDataOffset()), HeapReg); #endif @@ -6249,17 +6251,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co // 2.1. Get ExitDatum unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex); #if defined(JS_CODEGEN_X64) m.masm().append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset)); #elif defined(JS_CODEGEN_X86) m.masm().append(AsmJSGlobalAccess(masm.movlWithPatch(Imm32(0), callee), globalDataOffset)); #else - masm.computeEffectiveAddress(Address(GlobalReg, globalDataOffset), callee); + masm.computeEffectiveAddress(Address(GlobalReg, globalDataOffset - AsmJSGlobalRegBias), callee); #endif // 2.2. Get callee masm.loadPtr(Address(callee, offsetof(AsmJSModule::ExitDatum, fun)), callee); // 2.3. Save callee masm.storePtr(callee, Address(StackPointer, argOffset)); argOffset += sizeof(size_t);
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -22,19 +22,19 @@ #include "vm/StringBuffer.h" #include "vm/TypedArrayObject.h" #include "jsatominlines.h" #include "jsobjinlines.h" #include "vm/Shape-inl.h" +using mozilla::AssertedCast; using mozilla::CheckedInt32; using mozilla::DebugOnly; -using mozilla::SafeCast; using namespace js; const Class js::TypedObjectModuleObject::class_ = { "TypedObject", JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject), JS_PropertyStub, /* addProperty */ @@ -1157,26 +1157,26 @@ StructTypeDescr::fieldName(size_t index) } size_t StructTypeDescr::fieldOffset(size_t index) const { JSObject &fieldOffsets = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject(); JS_ASSERT(index < fieldOffsets.getDenseInitializedLength()); - return SafeCast<size_t>(fieldOffsets.getDenseElement(index).toInt32()); + return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32()); } size_t StructTypeDescr::maybeForwardedFieldOffset(size_t index) const { JSObject &fieldOffsets = *MaybeForwarded(&getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject()); JS_ASSERT(index < fieldOffsets.getDenseInitializedLength()); - return SafeCast<size_t>(fieldOffsets.getDenseElement(index).toInt32()); + return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32()); } SizedTypeDescr& StructTypeDescr::fieldDescr(size_t index) const { JSObject &fieldDescrs = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES).toObject(); JS_ASSERT(index < fieldDescrs.getDenseInitializedLength());
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -140,16 +140,19 @@ IonContext::~IonContext() } bool jit::InitializeIon() { if (!TlsIonContext.initialized() && !TlsIonContext.init()) return false; CheckLogging(); +#if defined(JS_CODEGEN_ARM) + InitARMFlags(); +#endif CheckPerf(); return true; } JitRuntime::JitRuntime() : execAlloc_(nullptr), ionAlloc_(nullptr), exceptionTail_(nullptr),
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -28,19 +28,19 @@ #include "jsscriptinlines.h" #include "jit/CompileInfo-inl.h" #include "jit/ExecutionMode-inl.h" using namespace js; using namespace js::jit; +using mozilla::AssertedCast; using mozilla::DebugOnly; using mozilla::Maybe; -using mozilla::SafeCast; class jit::BaselineFrameInspector { public: types::Type thisType; JSObject *singletonScopeChain; Vector<types::Type, 4, IonAllocPolicy> argTypes; @@ -7547,17 +7547,17 @@ IonBuilder::addTypedArrayLengthAndData(M // The 'data' pointer can change in rare circumstances // (ArrayBufferObject::changeContents). types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr); if (!tarrType->unknownProperties()) { tarrType->watchStateChangeForTypedArrayData(constraints()); obj->setImplicitlyUsedUnchecked(); - int32_t len = SafeCast<int32_t>(tarr->length()); + int32_t len = AssertedCast<int32_t>(tarr->length()); *length = MConstant::New(alloc(), Int32Value(len)); current->add(*length); if (index) { if (checking == DoBoundsCheck) *index = addBoundsCheck(*index, *length); *elements = MConstantElements::New(alloc(), data);
--- a/js/src/jit/arm/Architecture-arm.cpp +++ b/js/src/jit/arm/Architecture-arm.cpp @@ -11,18 +11,18 @@ #endif #include <fcntl.h> #include <unistd.h> #include "jit/arm/Assembler-arm.h" #include "jit/RegisterSets.h" -#if defined(ANDROID) || defined(MOZ_B2G) || defined(JS_ARM_SIMULATOR) -// The Android NDK does not include the hwcap.h kernel header, and it is not +#if defined(ANDROID) || defined(JS_ARM_SIMULATOR) +// The Android NDK and B2G do not include the hwcap.h kernel header, and it is not // defined when building the simulator, so inline the header defines we need. # define HWCAP_VFP (1 << 6) # define HWCAP_NEON (1 << 12) # define HWCAP_VFPv3 (1 << 13) # define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */ # define HWCAP_VFPv4 (1 << 16) # define HWCAP_IDIVA (1 << 17) # define HWCAP_IDIVT (1 << 18) @@ -33,30 +33,37 @@ # if !defined(HWCAP_IDIVA) # define HWCAP_IDIVA (1 << 17) # endif # if !defined(HWCAP_VFPD32) # define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ # endif #endif -// Not part of the HWCAP flag, but we need to know this, and this bit is not -// used so we are using it. +// Not part of the HWCAP flag, but we need to know these and these bits are not used. + +// A bit to flag the use of the ARMv7 arch, otherwise ARMv6. #define HWCAP_ARMv7 (1 << 28) -// Also take a bit to flag the use of the hardfp ABI. +// A bit to flag the use of the hardfp ABI. #define HWCAP_USE_HARDFP_ABI (1 << 27) +// A bit to flag when alignment faults are enabled and signal. +#define HWCAP_ALIGNMENT_FAULT (1 << 26) + +// A bit to flag when the flags are uninitialized, so they can be atomically set. +#define HWCAP_UNINITIALIZED (1 << 25) + namespace js { namespace jit { // Parse the Linux kernel cpuinfo features. This is also used to parse the -// override features which has some extensions: 'armv7' and 'hardfp'. -uint32_t +// override features which has some extensions: 'armv7', 'align' and 'hardfp'. +static uint32_t ParseARMCpuFeatures(const char *features, bool override = false) { uint32_t flags = 0; for (;;) { char ch = *features; if (!ch) { // End of string. @@ -88,107 +95,138 @@ ParseARMCpuFeatures(const char *features else if (count == 5 && strncmp(features, "idiva", 5) == 0) flags |= HWCAP_IDIVA; else if (count == 5 && strncmp(features, "idivt", 5) == 0) flags |= HWCAP_IDIVT; else if (count == 6 && strncmp(features, "vfpd32", 6) == 0) flags |= HWCAP_VFPD32; else if (count == 5 && strncmp(features, "armv7", 5) == 0) flags |= HWCAP_ARMv7; + else if (count == 5 && strncmp(features, "align", 5) == 0) + flags |= HWCAP_ALIGNMENT_FAULT; #if defined(JS_ARM_SIMULATOR) else if (count == 6 && strncmp(features, "hardfp", 6) == 0) flags |= HWCAP_USE_HARDFP_ABI; #endif else if (override) fprintf(stderr, "Warning: unexpected ARM feature at: %s\n", features); features = end; } - IonSpew(IonSpew_Codegen, "ARM features: '%s'\n flags: 0x%x\n", features, flags); + return flags; +} + +static uint32_t +CanonicalizeARMHwCapFlags(uint32_t flags) +{ + // Canonicalize the flags. These rules are also applied to the features + // supplied for simulation. + + // The VFPv3 feature is expected when the VFPv3D16 is reported, but add it + // just in case of a kernel difference in feature reporting. + if (flags & HWCAP_VFPv3D16) + flags |= HWCAP_VFPv3; + + // If VFPv3 or Neon is supported then this must be an ARMv7. + if (flags & (HWCAP_VFPv3 | HWCAP_NEON)) + flags |= HWCAP_ARMv7; + + // Some old kernels report VFP and not VFPv3, but if ARMv7 then it must be + // VFPv3. + if (flags & HWCAP_VFP && flags & HWCAP_ARMv7) + flags |= HWCAP_VFPv3; + + // Older kernels do not implement the HWCAP_VFPD32 flag. + if ((flags & HWCAP_VFPv3) && !(flags & HWCAP_VFPv3D16)) + flags |= HWCAP_VFPD32; + return flags; } // The override flags parsed from the ARMHWCAP environment variable or from the // --arm-hwcap js shell argument. -volatile static uint32_t armHwCapFlags = 0; +volatile static uint32_t armHwCapFlags = HWCAP_UNINITIALIZED; bool ParseARMHwCapFlags(const char *armHwCap) { uint32_t flags = 0; - if (!armHwCap || !armHwCap[0]) + if (!armHwCap) return false; -#ifdef JS_CODEGEN_ARM_HARDFP - flags |= HWCAP_USE_HARDFP_ABI; -#endif - if (strstr(armHwCap, "help")) { fflush(NULL); printf( "\n" "usage: ARMHWCAP=option,option,option,... where options can be:\n" "\n" - " armv7 \n" " vfp \n" " neon \n" " vfpv3 \n" " vfpv3d16 \n" " vfpv4 \n" " idiva \n" " idivt \n" " vfpd32 \n" + " armv7 \n" + " align \n" #if defined(JS_ARM_SIMULATOR) " hardfp \n" #endif "\n" ); exit(0); /*NOTREACHED*/ } - armHwCapFlags = ParseARMCpuFeatures(armHwCap, /* override = */ true); + flags = ParseARMCpuFeatures(armHwCap, /* override = */ true); + +#ifdef JS_CODEGEN_ARM_HARDFP + flags |= HWCAP_USE_HARDFP_ABI; +#endif + + armHwCapFlags = CanonicalizeARMHwCapFlags(flags); + IonSpew(IonSpew_Codegen, "ARM HWCAP: 0x%x\n", armHwCapFlags); return true; } -uint32_t GetARMFlags() +void +InitARMFlags() { - volatile static bool isSet = false; - volatile static uint32_t flags = 0; - if (isSet) - return flags; + uint32_t flags = 0; + + if (armHwCapFlags != HWCAP_UNINITIALIZED) + return; const char *env = getenv("ARMHWCAP"); - if (ParseARMHwCapFlags(env) || armHwCapFlags) { - flags = armHwCapFlags; - isSet = true; - return flags; - } + if (ParseARMHwCapFlags(env)) + return; #ifdef JS_ARM_SIMULATOR flags = HWCAP_ARMv7 | HWCAP_VFP | HWCAP_VFPv3 | HWCAP_VFPv4 | HWCAP_NEON; #else -#if defined(__linux__) || defined(ANDROID) || defined(MOZ_B2G) +#if defined(__linux__) + // This includes Android and B2G. bool readAuxv = false; int fd = open("/proc/self/auxv", O_RDONLY); if (fd > 0) { struct { uint32_t a_type; uint32_t a_val; } aux; while (read(fd, &aux, sizeof(aux))) { if (aux.a_type == AT_HWCAP) { flags = aux.a_val; readAuxv = true; break; } } close(fd); } if (!readAuxv) { - // Read the Features if the auxv is not available. + // Read the cpuinfo Features if the auxv is not available. FILE *fp = fopen("/proc/cpuinfo", "r"); if (fp) { char buf[1024]; memset(buf, 0, sizeof(buf)); size_t len = fread(buf, sizeof(char), sizeof(buf) - 1, fp); fclose(fp); buf[len] = '\0'; char *featureList = strstr(buf, "Features"); @@ -218,70 +256,73 @@ uint32_t GetARMFlags() #if defined(__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__) // Compiled to use ARMv7 instructions so assume the ARMv7 arch. flags |= HWCAP_ARMv7; #endif #endif // JS_ARM_SIMULATOR - // Canonicalize the flags. These rules are also applied to the features - // supplied for simulation. + armHwCapFlags = CanonicalizeARMHwCapFlags(flags); - // The VFPv3 feature is expected when the VFPv3D16 is reported, but add it - // just in case of a kernel difference in feature reporting. - if (flags & HWCAP_VFPv3D16) - flags |= HWCAP_VFPv3; - - // If VFPv3 or Neon is supported then this must be an ARMv7. - if (flags & (HWCAP_VFPv3 | HWCAP_NEON)) - flags |= HWCAP_ARMv7; + IonSpew(IonSpew_Codegen, "ARM HWCAP: 0x%x\n", armHwCapFlags); + return; +} - // Some old kernels report VFP and not VFPv3, but if ARMv7 then it must be - // VFPv3. - if (flags & HWCAP_VFP && flags & HWCAP_ARMv7) - flags |= HWCAP_VFPv3; - - // Older kernels do not implement the HWCAP_VFPD32 flag. - if ((flags & HWCAP_VFPv3) && !(flags & HWCAP_VFPv3D16)) - flags |= HWCAP_VFPD32; - - IonSpew(IonSpew_Codegen, "ARM HWCAP: 0x%x\n", flags); - isSet = true; - return flags; +uint32_t +GetARMFlags() +{ + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags; } bool HasMOVWT() { - return GetARMFlags() & HWCAP_ARMv7; + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags & HWCAP_ARMv7; } + bool HasVFPv3() { - return GetARMFlags() & HWCAP_VFPv3; + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags & HWCAP_VFPv3; } + bool HasVFP() { - return GetARMFlags() & HWCAP_VFP; + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags & HWCAP_VFP; } bool Has32DP() { - return GetARMFlags() & HWCAP_VFPD32; + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags & HWCAP_VFPD32; } bool HasIDIV() { - return GetARMFlags() & HWCAP_IDIVA; + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags & HWCAP_IDIVA; +} + +// Returns true when cpu alignment faults are enabled and signaled, and thus we +// should ensure loads and stores are aligned. +bool HasAlignmentFault() +{ + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags & HWCAP_ALIGNMENT_FAULT; } // This is defined in the header and inlined when not using the simulator. #if defined(JS_ARM_SIMULATOR) bool UseHardFpABI() { - return GetARMFlags() & HWCAP_USE_HARDFP_ABI; + MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED); + return armHwCapFlags & HWCAP_USE_HARDFP_ABI; } #endif Registers::Code Registers::FromName(const char *name) { // Check for some register aliases first. if (strcmp(name, "ip") == 0)
--- a/js/src/jit/arm/Architecture-arm.h +++ b/js/src/jit/arm/Architecture-arm.h @@ -9,18 +9,18 @@ #include "mozilla/MathAlgorithms.h" #include <limits.h> #include <stdint.h> #include "js/Utility.h" -// Gcc appears to use __ARM_PCS_VFP to denote that the target is a hard-float -// target. +// GCC versions 4.6 and above define __ARM_PCS_VFP to denote a hard-float +// ABI target. #if defined(__ARM_PCS_VFP) #define JS_CODEGEN_ARM_HARDFP #endif namespace js { namespace jit { // In bytes: slots needed for potential memory->memory move spills. @@ -514,16 +514,17 @@ class VFPRegister typedef VFPRegister FloatRegister; uint32_t GetARMFlags(); bool HasMOVWT(); bool HasVFPv3(); bool HasVFP(); bool Has32DP(); bool HasIDIV(); +bool HasAlignmentFault(); // Arm/D32 has double registers that can NOT be treated as float32 and this // requires some dances in lowering. inline bool hasUnaliasedDouble() { return Has32DP(); } @@ -532,16 +533,18 @@ hasUnaliasedDouble() // a double as a temporary, you need a temporary double register. inline bool hasMultiAlias() { return true; } bool ParseARMHwCapFlags(const char *armHwCap); +void InitARMFlags(); +uint32_t GetARMFlags(); // If the simulator is used then the ABI choice is dynamic. Otherwise the ABI is // static and useHardFpABI is inlined so that unused branches can be optimized // away. #if defined(JS_ARM_SIMULATOR) bool UseHardFpABI(); #else static inline bool UseHardFpABI()
--- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -97,16 +97,21 @@ static MOZ_CONSTEXPR_VAR FloatRegister R static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = { FloatRegisters::d0, VFPRegister::Double}; static MOZ_CONSTEXPR_VAR FloatRegister ReturnSimdReg = InvalidFloatReg; static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = { FloatRegisters::d30, VFPRegister::Single }; static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = { FloatRegisters::d15, VFPRegister::Double }; static MOZ_CONSTEXPR_VAR FloatRegister ScratchSimdReg = InvalidFloatReg; static MOZ_CONSTEXPR_VAR FloatRegister ScratchUIntReg = { FloatRegisters::d15, VFPRegister::UInt }; static MOZ_CONSTEXPR_VAR FloatRegister ScratchIntReg = { FloatRegisters::d15, VFPRegister::Int }; +// A bias applied to the GlobalReg to allow the use of instructions with small +// negative immediate offsets which doubles the range of global data that can be +// accessed with a single instruction. +static const int32_t AsmJSGlobalRegBias = 1024; + // Registers used in the GenerateFFIIonExit Enable Activation block. static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r4; static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = r0; static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = r1; static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = r2; static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = r3; // Registers used in the GenerateFFIIonExit Disable Activation block.
--- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1758,27 +1758,16 @@ CodeGeneratorARM::generateInvalidateEpil void DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) { // Can always use the scratch register on ARM. addState->dispatchScratch = ScratchRegister; } -template <class U> -Register -getBase(U *mir) -{ - switch (mir->base()) { - case U::Heap: return HeapReg; - case U::Global: return GlobalReg; - } - return InvalidReg; -} - bool CodeGeneratorARM::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins) { MOZ_ASSUME_UNREACHABLE("NYI"); } bool CodeGeneratorARM::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins) @@ -1840,20 +1829,22 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs return true; } BufferOffset bo = masm.ma_BoundsCheck(ptrReg); if (isFloat) { FloatRegister dst = ToFloatRegister(ins->output()); VFPRegister vd(dst); if (size == 32) { - masm.ma_vldr(Operand(GlobalReg, AsmJSNaN32GlobalDataOffset), vd.singleOverlay(), Assembler::AboveOrEqual); + masm.ma_vldr(Operand(GlobalReg, AsmJSNaN32GlobalDataOffset - AsmJSGlobalRegBias), + vd.singleOverlay(), Assembler::AboveOrEqual); masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below); } else { - masm.ma_vldr(Operand(GlobalReg, AsmJSNaN64GlobalDataOffset), vd, Assembler::AboveOrEqual); + masm.ma_vldr(Operand(GlobalReg, AsmJSNaN64GlobalDataOffset - AsmJSGlobalRegBias), + vd, Assembler::AboveOrEqual); masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Below); } } else { Register d = ToRegister(ins->output()); masm.ma_mov(Imm32(0), d, NoSetCond, Assembler::AboveOrEqual); masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, d, Offset, Assembler::Below); } masm.append(AsmJSHeapAccess(bo.getOffset())); @@ -2063,17 +2054,17 @@ CodeGeneratorARM::visitEffectiveAddress( masm.ma_add(Imm32(mir->displacement()), output); return true; } bool CodeGeneratorARM::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins) { const MAsmJSLoadGlobalVar *mir = ins->mir(); - unsigned addr = mir->globalDataOffset(); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; if (mir->type() == MIRType_Int32) { masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output())); } else if (mir->type() == MIRType_Float32) { VFPRegister vd(ToFloatRegister(ins->output())); masm.ma_vldr(Operand(GlobalReg, addr), vd.singleOverlay()); } else { masm.ma_vldr(Operand(GlobalReg, addr), ToFloatRegister(ins->output())); } @@ -2082,17 +2073,17 @@ CodeGeneratorARM::visitAsmJSLoadGlobalVa bool CodeGeneratorARM::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) { const MAsmJSStoreGlobalVar *mir = ins->mir(); MIRType type = mir->value()->type(); JS_ASSERT(IsNumberType(type)); - unsigned addr = mir->globalDataOffset(); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; if (mir->value()->type() == MIRType_Int32) { masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value())); } else if (mir->value()->type() == MIRType_Float32) { VFPRegister vd(ToFloatRegister(ins->value())); masm.ma_vstr(vd.singleOverlay(), Operand(GlobalReg, addr)); } else { masm.ma_vstr(ToFloatRegister(ins->value()), Operand(GlobalReg, addr)); } @@ -2103,29 +2094,30 @@ bool CodeGeneratorARM::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins) { const MAsmJSLoadFuncPtr *mir = ins->mir(); Register index = ToRegister(ins->index()); Register tmp = ToRegister(ins->temp()); Register out = ToRegister(ins->output()); unsigned addr = mir->globalDataOffset(); - masm.ma_mov(Imm32(addr), tmp); + masm.ma_mov(Imm32(addr - AsmJSGlobalRegBias), tmp); masm.as_add(tmp, tmp, lsl(index, 2)); masm.ma_ldr(DTRAddr(GlobalReg, DtrRegImmShift(tmp, LSL, 0)), out); return true; } bool CodeGeneratorARM::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins) { const MAsmJSLoadFFIFunc *mir = ins->mir(); - masm.ma_ldr(Operand(GlobalReg, mir->globalDataOffset()), ToRegister(ins->output())); + masm.ma_ldr(Operand(GlobalReg, mir->globalDataOffset() - AsmJSGlobalRegBias), + ToRegister(ins->output())); return true; } bool CodeGeneratorARM::visitNegI(LNegI *ins) { Register input = ToRegister(ins->input());
--- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -1622,17 +1622,17 @@ class MacroAssemblerARMCompat : public M } #ifdef JSGC_GENERATIONAL void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label *label); void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label *label); #endif void loadAsmJSActivation(Register dest) { - loadPtr(Address(GlobalReg, AsmJSActivationGlobalDataOffset), dest); + loadPtr(Address(GlobalReg, AsmJSActivationGlobalDataOffset - AsmJSGlobalRegBias), dest); } }; typedef MacroAssemblerARMCompat MacroAssemblerSpecific; } // namespace jit } // namespace js
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1652,17 +1652,17 @@ const size_t TypedArrayLengthSlot = 1; */ #define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \ inline void \ Get ## Type ## ArrayLengthAndData(JSObject *obj, uint32_t *length, type **data) \ { \ JS_ASSERT(GetObjectClass(obj) == detail::Type ## ArrayClassPtr); \ const JS::Value &slot = GetReservedSlot(obj, detail::TypedArrayLengthSlot); \ - *length = mozilla::SafeCast<uint32_t>(slot.toInt32()); \ + *length = mozilla::AssertedCast<uint32_t>(slot.toInt32()); \ *data = static_cast<type*>(GetObjectPrivate(obj)); \ } JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t) JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t) JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t) JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t) JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -53,25 +53,25 @@ using namespace js; using namespace js::gc; using namespace js::types; using namespace js::unicode; using JS::Symbol; using JS::SymbolCode; +using mozilla::AssertedCast; using mozilla::CheckedInt; using mozilla::IsNaN; using mozilla::IsNegativeZero; using mozilla::IsSame; using mozilla::Move; using mozilla::PodCopy; using mozilla::PodEqual; using mozilla::RangedPtr; -using mozilla::SafeCast; using mozilla::UniquePtr; using JS::AutoCheckCannotGC; static JSLinearString * ArgToRootedString(JSContext *cx, CallArgs &args, unsigned argno) { if (argno >= args.length()) @@ -235,17 +235,17 @@ template <typename CharT> static bool Unescape(StringBuffer &sb, const mozilla::Range<const CharT> chars) { /* * NB: use signed integers for length/index to allow simple length * comparisons without unsigned-underflow hazards. */ static_assert(JSString::MAX_LENGTH <= INT_MAX, "String length must fit in a signed integer"); - int length = SafeCast<int>(chars.length()); + int length = AssertedCast<int>(chars.length()); /* * Note that the spec algorithm has been optimized to avoid building * a string in the case where no escapes are present. */ /* Step 4. */ int k = 0; @@ -1009,17 +1009,17 @@ str_normalize(JSContext *cx, unsigned ar // Step 8. AutoStableStringChars stableChars(cx); if (!str->ensureFlat(cx) || !stableChars.initTwoByte(cx, str)) return false; static const size_t INLINE_CAPACITY = 32; const UChar *srcChars = JSCharToUChar(stableChars.twoByteRange().start().get()); - int32_t srcLen = SafeCast<int32_t>(str->length()); + int32_t srcLen = AssertedCast<int32_t>(str->length()); Vector<jschar, INLINE_CAPACITY> chars(cx); if (!chars.resize(INLINE_CAPACITY)) return false; UErrorCode status = U_ZERO_ERROR; int32_t size = unorm_normalize(srcChars, srcLen, form, 0, JSCharToUChar(chars.begin()), INLINE_CAPACITY, &status);
--- a/js/src/vm/NumericConversions.h +++ b/js/src/vm/NumericConversions.h @@ -47,17 +47,17 @@ ToUintWidth(double d) int_fast16_t((bits & mozilla::FloatingPoint<double>::kExponentBits) >> DoubleExponentShift) - int_fast16_t(mozilla::FloatingPoint<double>::kExponentBias); // If the exponent's less than zero, abs(d) < 1, so the result is 0. (This // also handles subnormals.) if (exp < 0) return 0; - uint_fast16_t exponent = mozilla::SafeCast<uint_fast16_t>(exp); + uint_fast16_t exponent = mozilla::AssertedCast<uint_fast16_t>(exp); // If the exponent is greater than or equal to the bits of precision of a // double plus ResultType's width, the number is either infinite, NaN, or // too large to have lower-order bits in the congruent value. (Example: // 2**84 is exactly representable as a double. The next exact double is // 2**84 + 2**32. Thus if ResultType is int32_t, an exponent >= 84 implies // floor(abs(d)) == 0 mod 2**32.) Return 0 in all these cases. const size_t ResultWidth = CHAR_BIT * sizeof(ResultType);
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2764,17 +2764,19 @@ JSReporter::CollectReports(WindowPaths * anonymize); OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject); if (!JS::CollectRuntimeStats(xpcrt->Runtime(), &rtStats, &orphanReporter, anonymize)) { return NS_ERROR_FAILURE; } - size_t xpconnect = xpcrt->SizeOfIncludingThis(JSMallocSizeOf); + size_t xpcJSRuntimeSize = xpcrt->SizeOfIncludingThis(JSMallocSizeOf); + + size_t wrappedJSSize = xpcrt->GetWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf); XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf); XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo); mozJSComponentLoader* loader = mozJSComponentLoader::Get(); size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0; // This is the second step (see above). First we report stuff in the @@ -2844,19 +2846,23 @@ JSReporter::CollectReports(WindowPaths * REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"), KIND_OTHER, rtStats.gcHeapGCThings, "GC things: objects, strings, scripts, etc."); // Report xpconnect. REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"), - KIND_HEAP, xpconnect, + KIND_HEAP, xpcJSRuntimeSize, "The XPConnect runtime."); + REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/wrappedjs"), + KIND_HEAP, wrappedJSSize, + "Wrappers used to implement XPIDL interfaces with JS."); + REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"), KIND_HEAP, sizeInfo.mScopeAndMapSize, "XPConnect scopes."); REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"), KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize, "Prototype and interface binding caches.");
--- a/js/xpconnect/src/XPCMaps.cpp +++ b/js/xpconnect/src/XPCMaps.cpp @@ -106,16 +106,33 @@ JSObject2WrappedJSMap::ShutdownMarker() for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) { nsXPCWrappedJS* wrapper = r.front().value(); MOZ_ASSERT(wrapper, "found a null JS wrapper!"); MOZ_ASSERT(wrapper->IsValid(), "found an invalid JS wrapper!"); wrapper->SystemIsBeingShutDown(); } } +size_t +JSObject2WrappedJSMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + size_t n = mallocSizeOf(this); + n += mTable.sizeOfExcludingThis(mallocSizeOf); + return n; +} + +size_t +JSObject2WrappedJSMap::SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const +{ + size_t n = 0; + for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) + n += r.front().value()->SizeOfIncludingThis(mallocSizeOf); + return n; +} + /***************************************************************************/ // implement Native2WrappedNativeMap... // static Native2WrappedNativeMap* Native2WrappedNativeMap::newMap(int length) { Native2WrappedNativeMap* map = new Native2WrappedNativeMap(length);
--- a/js/xpconnect/src/XPCMaps.h +++ b/js/xpconnect/src/XPCMaps.h @@ -67,21 +67,21 @@ public: for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) r.front().value()->DebugDump(depth); } void FindDyingJSObjects(nsTArray<nsXPCWrappedJS*>* dying); void ShutdownMarker(); - size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { - size_t n = mallocSizeOf(this); - n += mTable.sizeOfExcludingThis(mallocSizeOf); - return n; - } + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + // Report the sum of SizeOfIncludingThis() for all wrapped JS in the map. + // Each wrapped JS is only in one map. + size_t SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const; private: JSObject2WrappedJSMap() {} /* * This function is called during minor GCs for each key in the HashMap that * has been moved. */
--- a/js/xpconnect/src/XPCWrappedJS.cpp +++ b/js/xpconnect/src/XPCWrappedJS.cpp @@ -547,16 +547,35 @@ nsXPCWrappedJS::SystemIsBeingShutDown() // at this point. *mJSObj.unsafeGet() = nullptr; // Notify other wrappers in the chain. if (mNext) mNext->SystemIsBeingShutDown(); } +size_t +nsXPCWrappedJS::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + // mJSObject is a JS pointer, so don't measure the object. + // mClass is not uniquely owned by this WrappedJS. Measure it in IID2WrappedJSClassMap. + // mRoot is not measured because it is either |this| or we have already measured it. + // mOuter is rare and probably not uniquely owned by this. + size_t n = mallocSizeOf(this); + n += nsAutoXPTCStub::SizeOfExcludingThis(mallocSizeOf); + + // Wrappers form a linked list via the mNext field, so include them all + // in the measurement. Only root wrappers are stored in the map, so + // everything will be measured exactly once. + if (mNext) + n += mNext->SizeOfIncludingThis(mallocSizeOf); + + return n; +} + /***************************************************************************/ /* readonly attribute nsISimpleEnumerator enumerator; */ NS_IMETHODIMP nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate) { AutoJSContext cx; XPCCallContext ccx(NATIVE_CALLER, cx);
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2484,16 +2484,18 @@ public: return; } mRoot->mOuter = aNative; } void TraceJS(JSTracer* trc); static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize); + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + virtual ~nsXPCWrappedJS(); protected: nsXPCWrappedJS(); // not implemented nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj, nsXPCWrappedJSClass* aClass, nsXPCWrappedJS* root);
--- a/layout/base/Units.h +++ b/layout/base/Units.h @@ -290,17 +290,27 @@ struct RenderTargetPixel { * generally be represented in ScreenPixel units. */ struct ScreenPixel { static ScreenIntPoint FromUntyped(const nsIntPoint& aPoint) { return ScreenIntPoint(aPoint.x, aPoint.y); } }; -// Operators to apply ScaleFactors directly to Points, Rects, Sizes and Margins +// Operators to apply ScaleFactors directly to Coords, Points, Rects, Sizes and Margins + +template<class src, class dst> +gfx::CoordTyped<dst> operator*(gfx::CoordTyped<src>& aCoord, const gfx::ScaleFactor<src, dst>& aScale) { + return gfx::CoordTyped<dst>(aCoord.value * aScale.scale); +} + +template<class src, class dst> +gfx::CoordTyped<dst> operator/(gfx::CoordTyped<src>& aCoord, const gfx::ScaleFactor<src, dst>& aScale) { + return gfx::CoordTyped<dst>(aCoord.value / aScale.scale); +} template<class src, class dst> gfx::PointTyped<dst> operator*(const gfx::PointTyped<src>& aPoint, const gfx::ScaleFactor<src, dst>& aScale) { return gfx::PointTyped<dst>(aPoint.x * aScale.scale, aPoint.y * aScale.scale); } template<class src, class dst>
--- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -1073,17 +1073,17 @@ dom::HTMLOptionElement* nsListControlFrame::GetCurrentOption() { // The mEndSelectionIndex is what is currently being selected. Use // the selected index if this is kNothingSelected. int32_t focusedIndex = (mEndSelectionIndex == kNothingSelected) ? GetSelectedIndex() : mEndSelectionIndex; if (focusedIndex != kNothingSelected) { - return GetOption(SafeCast<uint32_t>(focusedIndex)); + return GetOption(AssertedCast<uint32_t>(focusedIndex)); } // There is no selected item. Return the first non-disabled item. nsRefPtr<dom::HTMLSelectElement> selectElement = dom::HTMLSelectElement::FromContent(mContent); for (uint32_t i = 0, length = selectElement->Length(); i < length; ++i) { dom::HTMLOptionElement* node = selectElement->Item(i); @@ -1901,17 +1901,17 @@ void nsListControlFrame::ScrollToIndex(int32_t aIndex) { if (aIndex < 0) { // XXX shouldn't we just do nothing if we're asked to scroll to // kNothingSelected? ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT); } else { nsRefPtr<dom::HTMLOptionElement> option = - GetOption(SafeCast<uint32_t>(aIndex)); + GetOption(AssertedCast<uint32_t>(aIndex)); if (option) { ScrollToFrame(*option); } } } void nsListControlFrame::ScrollToFrame(dom::HTMLOptionElement& aOptElement)
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-darken-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Darken an HTML Element Using the Brightness Function</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(0, 64, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a dark green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-darken.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Darken an HTML Element Using the Brightness Function</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-brightness"> + <link rel="match" href="brightness-darken-ref.html"> + <meta name="assert" + content="Given a factor less than one, the CSS brightness filter + function should darken the color of an HTML element."> + <style type="text/css"> + #target { + filter: brightness(0.25); + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a dark green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-extreme-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element Using a Large Factor</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-extreme.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element Using a Large Factor</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-brightness"> + <link rel="match" href="brightness-extreme-ref.html"> + <meta name="assert" + content="Given a large factor, the CSS brightness filter function should + completely change the color of an HTML element."> + <style type="text/css"> + #target { + filter: brightness(1000); + background-color: rgb(0, 1, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-one-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(0, 128, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-one.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-brightness"> + <link rel="match" href="brightness-one-ref.html"> + <meta name="assert" + content="Given a factor of one, the CSS brightness filter function + should not change the color of an HTML element."> + <style type="text/css"> + #target { + filter: brightness(1); + background-color: rgb(0, 128, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-percent-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element Using a Percentage</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(0, 128, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-percent.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element Using a Percentage</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-brightness"> + <link rel="match" href="brightness-percent-ref.html"> + <meta name="assert" + content="Given a percentage, the CSS brightness filter function should + change the color of an HTML element."> + <style type="text/css"> + #target { + filter: brightness(400%); + background-color: rgb(0, 32, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(0, 128, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-zero-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Darken an HTML Element Using the Brightness Function and a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: black; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a black square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness-zero.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Darken an HTML Element Using the Brightness Function and a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-brightness"> + <link rel="match" href="brightness-zero-ref.html"> + <meta name="assert" + content="Given a factor of zero, the CSS brightness filter function + should change the color of an HTML element to black."> + <style type="text/css"> + #target { + filter: brightness(0); + background-color: #f00; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a black square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/brightness.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Brighten an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-brightness"> + <link rel="match" href="brightness-ref.html"> + <meta name="assert" + content="The CSS brightness filter function should change the color of + an HTML element."> + <style type="text/css"> + #target { + filter: brightness(4); + background-color: rgb(0, 32, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-extreme-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Increase the Contrast of an HTML Element Using a Large Factor</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-extreme.html @@ -0,0 +1,29 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Increase the Contrast of an HTML Element Using a Large Factor</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-contrast"> + <link rel="match" href="contrast-extreme-ref.html"> + <meta name="assert" + content="Given a large factor, the CSS contrast filter function should + should change color channel values to be much farther from + their middle value."> + <style type="text/css"> + #target { + filter: contrast(1000); + background-color: rgb(127, 129, 127); /* 127 should change to 0, and 129 should change to 255. */ + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-one-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Set the Contrast of HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(0, 128, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-one.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Set the Contrast of HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-contrast"> + <link rel="match" href="contrast-one-ref.html"> + <meta name="assert" + content="Given a factor of one, the CSS contrast filter function + should not change the color of an HTML element."> + <style type="text/css"> + #target { + filter: contrast(1); + background-color: rgb(0, 128, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-percent-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Increase the Contrast of an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-percent.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Increase the Contrast of an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-contrast"> + <link rel="match" href="contrast-percent-ref.html"> + <meta name="assert" + content="Given a percentage, the CSS contrast filter function should + change the color of an HTML element."> + <style type="text/css"> + #target { + filter: contrast(200%); + background-color: rgb(0, 196, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-reduce-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Reduce the Contrast of an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(64, 191, 64); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-reduce.html @@ -0,0 +1,29 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Reduce the Contrast of an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-contrast"> + <link rel="match" href="contrast-reduce-ref.html"> + <meta name="assert" + content="Given a factor of less than one, the CSS contrast filter + function should change color channel values to be closer to + their middle value."> + <style type="text/css"> + #target { + filter: contrast(0.5); + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Increase the Contrast of an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-zero-ref.html @@ -0,0 +1,23 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Reduce the Contrast of an HTML Element Using a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + filter: contrast(0); + background-color: rgb(128, 128, 128); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a gray square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast-zero.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Reduce the Contrast of an HTML Element Using a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-contrast"> + <link rel="match" href="contrast-zero-ref.html"> + <meta name="assert" + content="Given a factor of zero, the CSS contrast filter function + should change the color of an HTML element to gray."> + <style type="text/css"> + #target { + filter: contrast(0); + background-color: rgb(0, 128, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a gray square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/contrast.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Increase the Contrast of an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-contrast"> + <link rel="match" href="contrast-ref.html"> + <meta name="assert" + content="The CSS contrast filter function should change the color of + an HTML element."> + <style type="text/css"> + #target { + filter: contrast(2); + background-color: rgb(0, 196, 0); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a bright green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-half-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor of One Half</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(127, 127, 127); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a gray square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-half.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor of One Half</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-invert"> + <link rel="match" href="invert-half-ref.html"> + <meta name="assert" + content="Given a factor of one half, the CSS invert filter function + should change the color of an HTML element to gray."> + <style type="text/css"> + #target { + filter: invert(0.5); + background-color: #f00; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a gray square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-one-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-one.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-invert"> + <link rel="match" href="invert-one-ref.html"> + <meta name="assert" + content="Given a factor of one, the CSS invert filter function should + completely change the color of an HTML element."> + <style type="text/css"> + #target { + filter: invert(1); + background-color: #f0f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-over-one-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor Over One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-over-one.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor Over One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-invert"> + <link rel="match" href="invert-over-one-ref.html"> + <meta name="assert" + content="Given a factor over one, the CSS invert filter function + should completely change the color of an HTML element."> + <style type="text/css"> + #target { + filter: invert(1000); + background-color: #f0f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-percent-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Percentage</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(63, 191, 63); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-percent.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Percentage</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-invert"> + <link rel="match" href="invert-percent-ref.html"> + <meta name="assert" + content="Given a percentage, the CSS invert filter function should + change the color of an HTML element."> + <style type="text/css"> + #target { + filter: invert(75%); + background-color: #f0f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(63, 191, 63); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-zero-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert-zero.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element Using a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-invert"> + <link rel="match" href="invert-zero-ref.html"> + <meta name="assert" + content="Given a factor of zero, the CSS invert filter function should + not change the color of an HTML element."> + <style type="text/css"> + #target { + filter: invert(0); + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/invert.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Invert an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-invert"> + <link rel="match" href="invert-ref.html"> + <meta name="assert" + content="The CSS invert filter function should change the color of an + HTML element."> + <style type="text/css"> + #target { + filter: invert(0.75); + background-color: #f0f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-one-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-one.html @@ -0,0 +1,36 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Factor of One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-opacity"> + <link rel="match" href="opacity-one-ref.html"> + <meta name="assert" + content="Given a factor of one, the CSS opacity filter function should + not change the opaqueness of an HTML element."> + <style type="text/css"> + #below-target { + background-color: #f00; + width: 100px; + height: 100px; + } + #target { + filter: opacity(1000); + background-color: #0f0; + position: relative; + top: -100px; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="below-target"></div> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-over-one-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Factor Over One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-over-one-translucent-source-ref.html @@ -0,0 +1,21 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to a Translucent HTML Element Using a Factor Over One</title> + <style type="text/css"> + #target { + background-color: rgba(0, 255, 0, 0.25); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a faded green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-over-one-translucent-source.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to a Translucent HTML Element Using a Factor Over One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-opacity"> + <link rel="match" href="opacity-over-one-translucent-source-ref.html"> + <meta name="assert" + content="Given a factor over one, the CSS opacity filter function should + not change the translucency of an HTML element."> + <style type="text/css"> + #target { + filter: opacity(1000); + background-color: rgba(0, 255, 0, 0.25); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a faded green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-over-one.html @@ -0,0 +1,36 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Factor Over One</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-opacity"> + <link rel="match" href="opacity-over-one-ref.html"> + <meta name="assert" + content="Given a factor over one, the CSS opacity filter function should + not change the opaqueness of an HTML element."> + <style type="text/css"> + #below-target { + background-color: #f00; + width: 100px; + height: 100px; + } + #target { + filter: opacity(1000); + background-color: #0f0; + position: relative; + top: -100px; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="below-target"></div> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-percent-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Percentage</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(192, 255, 192); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a faded green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-percent.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Percentage</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-opacity"> + <link rel="match" href="opacity-percent-ref.html"> + <meta name="assert" + content="Given a percentage, the CSS opacity filter function should make + an HTML element translucent."> + <style type="text/css"> + #target { + filter: opacity(25%); + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a faded green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + background-color: rgb(192, 255, 192); + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a faded green square.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-zero-ref.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #below-target { + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="below-target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity-zero.html @@ -0,0 +1,36 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element Using a Factor of Zero</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-opacity"> + <link rel="match" href="opacity-zero-ref.html"> + <meta name="assert" + content="Given a factor of zero, the CSS opacity filter function should + make an HTML element completely transparent."> + <style type="text/css"> + #below-target { + background-color: #0f0; + width: 100px; + height: 100px; + } + #target { + filter: opacity(0); + background-color: #f00; + position: relative; + top: -100px; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a green square.</p> + <div id="below-target"></div> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/opacity.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Apply Opacity to an HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-opacity"> + <link rel="match" href="opacity-ref.html"> + <meta name="assert" + content="The CSS opacity filter function should make an HTML element + translucent."> + <style type="text/css"> + #target { + filter: opacity(0.25); + background-color: #0f0; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a faded green square.</p> + <div id="target"></div> +</body> +</html>
--- a/layout/reftests/svg/filters/css-filters/reftest.list +++ b/layout/reftests/svg/filters/css-filters/reftest.list @@ -2,32 +2,56 @@ # e.g. filter: blur(3px) default-preferences pref(layout.css.filters.enabled,true) == blur.html blur-ref.html == blur.svg blur-ref.svg == blur-zero-radius.html blur-zero-radius-ref.html == blur-zoomed-page.html blur-zoomed-page-ref.html +== brightness.html brightness-ref.html +== brightness-darken.html brightness-darken-ref.html +== brightness-extreme.html brightness-extreme-ref.html +== brightness-one.html brightness-one-ref.html +== brightness-percent.html brightness-percent-ref.html +== brightness-zero.html brightness-zero-ref.html +== contrast.html contrast-ref.html +== contrast-extreme.html contrast-extreme-ref.html +== contrast-one.html contrast-one-ref.html +== contrast-percent.html contrast-percent-ref.html +== contrast-reduce.html contrast-reduce-ref.html +== contrast-zero.html contrast-zero-ref.html == drop-shadow.html drop-shadow-ref.html == drop-shadow-default-color.html drop-shadow-default-color-ref.html == drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html fuzzy-if(d2d,1,10000) == grayscale.html grayscale-ref.html fuzzy-if(d2d,1,10000) == grayscale-one.html grayscale-one-ref.html fuzzy-if(d2d,1,10000) == grayscale-over-one.html grayscale-over-one-ref.html fuzzy-if(d2d,1,10000) == grayscale-percent.html grayscale-percent-ref.html == grayscale-zero.html grayscale-zero-ref.html == hue-rotate.html hue-rotate-ref.html == hue-rotate-360.html hue-rotate-360-ref.html == hue-rotate-grad.html hue-rotate-grad-ref.html == hue-rotate-negative.html hue-rotate-negative-ref.html == hue-rotate-over-360.html hue-rotate-over-360-ref.html == hue-rotate-rad.html hue-rotate-rad-ref.html == hue-rotate-turn.html hue-rotate-turn-ref.html == hue-rotate-zero.html hue-rotate-zero-ref.html +fuzzy-if(d2d,1,10000) == invert.html invert-ref.html +== invert-half.html invert-half-ref.html +== invert-one.html invert-one-ref.html +== invert-over-one.html invert-over-one-ref.html +fuzzy-if(d2d,1,10000) == invert-percent.html invert-percent-ref.html +== invert-zero.html invert-zero-ref.html +fuzzy-if(d2d,1,10000) == opacity.html opacity-ref.html +== opacity-one.html opacity-one-ref.html +== opacity-over-one.html opacity-over-one-ref.html +== opacity-over-one-translucent-source.html opacity-over-one-translucent-source-ref.html +fuzzy-if(d2d,1,10000) == opacity-percent.html opacity-percent-ref.html +== opacity-zero.html opacity-zero-ref.html == saturate.html saturate-ref.html fuzzy-if(d2d,1,10000) == saturate-desaturate.html saturate-desaturate-ref.html == saturate-extreme.html saturate-extreme-ref.html == saturate-one.html saturate-one-ref.html == saturate-percent.html saturate-percent-ref.html fuzzy-if(d2d,1,10000) == saturate-zero.html saturate-zero-ref.html fuzzy-if(d2d,1,10000) == sepia.html sepia-ref.html fuzzy-if(d2d,1,10000) == sepia-one.html sepia-one-ref.html
--- a/layout/style/CSSStyleSheet.cpp +++ b/layout/style/CSSStyleSheet.cpp @@ -89,17 +89,17 @@ CSSRuleListImpl::GetParentObject() uint32_t CSSRuleListImpl::Length() { if (!mStyleSheet) { return 0; } - return SafeCast<uint32_t>(mStyleSheet->StyleRuleCount()); + return AssertedCast<uint32_t>(mStyleSheet->StyleRuleCount()); } nsIDOMCSSRule* CSSRuleListImpl::IndexedGetter(uint32_t aIndex, bool& aFound) { aFound = false; if (mStyleSheet) {
--- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -13776,16 +13776,17 @@ CSSParserImpl::ParseSingleFilter(nsCSSVa if (!GetToken(true)) { REPORT_UNEXPECTED_EOF(PEFilterEOF); return false; } if (mToken.mType != eCSSToken_Function) { REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); + UngetToken(); return false; } nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent); // Parse drop-shadow independently of the other filter functions // because of its more complex characteristics. if (functionName == eCSSKeyword_drop_shadow) { if (ParseDropShadow(aValue)) {
--- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -179,17 +179,17 @@ GroupRuleRuleList::GetParentObject() uint32_t GroupRuleRuleList::Length() { if (!mGroupRule) { return 0; } - return SafeCast<uint32_t>(mGroupRule->StyleRuleCount()); + return AssertedCast<uint32_t>(mGroupRule->StyleRuleCount()); } nsIDOMCSSRule* GroupRuleRuleList::IndexedGetter(uint32_t aIndex, bool& aFound) { aFound = false; if (mGroupRule) {
--- a/layout/svg/nsCSSFilterInstance.cpp +++ b/layout/svg/nsCSSFilterInstance.cpp @@ -46,35 +46,43 @@ nsCSSFilterInstance::BuildPrimitives(nsT nsresult result; switch(mFilter.GetType()) { case NS_STYLE_FILTER_BLUR: descr = CreatePrimitiveDescription(PrimitiveType::GaussianBlur, aPrimitiveDescrs); result = SetAttributesForBlur(descr); break; case NS_STYLE_FILTER_BRIGHTNESS: - return NS_ERROR_NOT_IMPLEMENTED; + descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); + result = SetAttributesForBrightness(descr); + break; case NS_STYLE_FILTER_CONTRAST: - return NS_ERROR_NOT_IMPLEMENTED; + descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); + result = SetAttributesForContrast(descr); + break; case NS_STYLE_FILTER_DROP_SHADOW: descr = CreatePrimitiveDescription(PrimitiveType::DropShadow, aPrimitiveDescrs); result = SetAttributesForDropShadow(descr); break; case NS_STYLE_FILTER_GRAYSCALE: descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); result = SetAttributesForGrayscale(descr); break; case NS_STYLE_FILTER_HUE_ROTATE: descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); result = SetAttributesForHueRotate(descr); break; case NS_STYLE_FILTER_INVERT: - return NS_ERROR_NOT_IMPLEMENTED; + descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); + result = SetAttributesForInvert(descr); + break; case NS_STYLE_FILTER_OPACITY: - return NS_ERROR_NOT_IMPLEMENTED; + descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); + result = SetAttributesForOpacity(descr); + break; case NS_STYLE_FILTER_SATURATE: descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); result = SetAttributesForSaturate(descr); break; case NS_STYLE_FILTER_SEPIA: descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); result = SetAttributesForSepia(descr); break; @@ -118,16 +126,67 @@ nsCSSFilterInstance::SetAttributesForBlu } Size radiusInFilterSpace = BlurRadiusToFilterSpace(radiusInFrameSpace.GetCoordValue()); aDescr.Attributes().Set(eGaussianBlurStdDeviation, radiusInFilterSpace); return NS_OK; } nsresult +nsCSSFilterInstance::SetAttributesForBrightness(FilterPrimitiveDescription& aDescr) +{ + const nsStyleCoord& styleValue = mFilter.GetFilterParameter(); + float value = styleValue.GetFactorOrPercentValue(); + + // Set transfer functions for RGB. + AttributeMap brightnessAttrs; + brightnessAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR); + brightnessAttrs.Set(eComponentTransferFunctionSlope, value); + brightnessAttrs.Set(eComponentTransferFunctionIntercept, 0.0f); + aDescr.Attributes().Set(eComponentTransferFunctionR, brightnessAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionG, brightnessAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionB, brightnessAttrs); + + // Set identity transfer function for A. + AttributeMap identityAttrs; + identityAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY); + aDescr.Attributes().Set(eComponentTransferFunctionA, identityAttrs); + + return NS_OK; +} + +nsresult +nsCSSFilterInstance::SetAttributesForContrast(FilterPrimitiveDescription& aDescr) +{ + const nsStyleCoord& styleValue = mFilter.GetFilterParameter(); + float value = styleValue.GetFactorOrPercentValue(); + float intercept = -(0.5 * value) + 0.5; + + // Set transfer functions for RGB. + AttributeMap contrastAttrs; + contrastAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR); + contrastAttrs.Set(eComponentTransferFunctionSlope, value); + contrastAttrs.Set(eComponentTransferFunctionIntercept, intercept); + aDescr.Attributes().Set(eComponentTransferFunctionR, contrastAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionG, contrastAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionB, contrastAttrs); + + // Set identity transfer function for A. + AttributeMap identityAttrs; + identityAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY); + aDescr.Attributes().Set(eComponentTransferFunctionA, identityAttrs); + + return NS_OK; +} + +nsresult nsCSSFilterInstance::SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr) { nsCSSShadowArray* shadows = mFilter.GetDropShadow(); if (!shadows || shadows->Length() != 1) { NS_NOTREACHED("Exactly one drop shadow should have been parsed."); return NS_ERROR_FAILURE; } @@ -173,16 +232,70 @@ nsCSSFilterInstance::SetAttributesForHue const nsStyleCoord& styleValue = mFilter.GetFilterParameter(); float value = styleValue.GetAngleValueInDegrees(); aDescr.Attributes().Set(eColorMatrixValues, &value, 1); return NS_OK; } nsresult +nsCSSFilterInstance::SetAttributesForInvert(FilterPrimitiveDescription& aDescr) +{ + const nsStyleCoord& styleValue = mFilter.GetFilterParameter(); + float value = ClampFactor(styleValue.GetFactorOrPercentValue()); + + // Set transfer functions for RGB. + AttributeMap invertAttrs; + float invertTableValues[2]; + invertTableValues[0] = value; + invertTableValues[1] = 1 - value; + invertAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE); + invertAttrs.Set(eComponentTransferFunctionTableValues, invertTableValues, 2); + aDescr.Attributes().Set(eComponentTransferFunctionR, invertAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionG, invertAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionB, invertAttrs); + + // Set identity transfer function for A. + AttributeMap identityAttrs; + identityAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY); + aDescr.Attributes().Set(eComponentTransferFunctionA, identityAttrs); + + return NS_OK; +} + +nsresult +nsCSSFilterInstance::SetAttributesForOpacity(FilterPrimitiveDescription& aDescr) +{ + const nsStyleCoord& styleValue = mFilter.GetFilterParameter(); + float value = ClampFactor(styleValue.GetFactorOrPercentValue()); + + // Set identity transfer functions for RGB. + AttributeMap identityAttrs; + identityAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY); + aDescr.Attributes().Set(eComponentTransferFunctionR, identityAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionG, identityAttrs); + aDescr.Attributes().Set(eComponentTransferFunctionB, identityAttrs); + + // Set transfer function for A. + AttributeMap opacityAttrs; + float opacityTableValues[2]; + opacityTableValues[0] = 0; + opacityTableValues[1] = value; + opacityAttrs.Set(eComponentTransferFunctionType, + (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE); + opacityAttrs.Set(eComponentTransferFunctionTableValues, opacityTableValues, 2); + aDescr.Attributes().Set(eComponentTransferFunctionA, opacityAttrs); + + return NS_OK; +} + +nsresult nsCSSFilterInstance::SetAttributesForSaturate(FilterPrimitiveDescription& aDescr) { // Set color matrix type. aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE); // Set color matrix values. const nsStyleCoord& styleValue = mFilter.GetFilterParameter(); float value = styleValue.GetFactorOrPercentValue();
--- a/layout/svg/nsCSSFilterInstance.h +++ b/layout/svg/nsCSSFilterInstance.h @@ -58,19 +58,23 @@ private: */ FilterPrimitiveDescription CreatePrimitiveDescription(PrimitiveType aType, const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs); /** * Sets aDescr's attributes using the style info in mFilter. */ nsresult SetAttributesForBlur(FilterPrimitiveDescription& aDescr); + nsresult SetAttributesForBrightness(FilterPrimitiveDescription& aDescr); + nsresult SetAttributesForContrast(FilterPrimitiveDescription& aDescr); nsresult SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr); nsresult SetAttributesForGrayscale(FilterPrimitiveDescription& aDescr); nsresult SetAttributesForHueRotate(FilterPrimitiveDescription& aDescr); + nsresult SetAttributesForInvert(FilterPrimitiveDescription& aDescr); + nsresult SetAttributesForOpacity(FilterPrimitiveDescription& aDescr); nsresult SetAttributesForSaturate(FilterPrimitiveDescription& aDescr); nsresult SetAttributesForSepia(FilterPrimitiveDescription& aDescr); /** * Returns the index of the last result in the aPrimitiveDescrs, which we'll * use as the input to this CSS filter. */ int32_t GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs);
--- a/media/mtransport/nr_socket_prsock.cpp +++ b/media/mtransport/nr_socket_prsock.cpp @@ -513,49 +513,54 @@ int NrSocket::sendto(const void *msg, si if(fd_==nullptr) ABORT(R_EOD); if (nr_is_stun_request_message((UCHAR*)msg, len)) { // Global rate limiting for stun requests, to mitigate the ice hammer DoS // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc) // Tolerate rate of 8k/sec, for one second. - static SimpleTokenBucket burst(8192*1, 8192); + static SimpleTokenBucket burst(16384*1, 16384); // Tolerate rate of 3.6k/sec over twenty seconds. static SimpleTokenBucket sustained(3686*20, 3686); // Check number of tokens in each bucket. if (burst.getTokens(UINT32_MAX) < len) { r_log(LOG_GENERIC, LOG_ERR, "Short term global rate limit for STUN requests exceeded."); +#ifdef MOZILLA_INTERNAL_API + nr_socket_short_term_violation_time = TimeStamp::Now(); +#endif + +// Bug 1013007 +#if !EARLY_BETA_OR_EARLIER + ABORT(R_WOULDBLOCK); +#else MOZ_ASSERT(false, "Short term global rate limit for STUN requests exceeded. Go " "bug bcampen@mozilla.com if you weren't intentionally " "spamming ICE candidates, or don't know what that means."); -#ifdef MOZILLA_INTERNAL_API - nr_socket_short_term_violation_time = TimeStamp::Now(); #endif - // TODO(bcampen@mozilla.com): Bug 1013007 Once we have better data on - // normal usage, re-enable this. - // ABORT(R_WOULDBLOCK); } if (sustained.getTokens(UINT32_MAX) < len) { r_log(LOG_GENERIC, LOG_ERR, "Long term global rate limit for STUN requests exceeded."); +#ifdef MOZILLA_INTERNAL_API + nr_socket_long_term_violation_time = TimeStamp::Now(); +#endif +// Bug 1013007 +#if !EARLY_BETA_OR_EARLIER + ABORT(R_WOULDBLOCK); +#else MOZ_ASSERT(false, "Long term global rate limit for STUN requests exceeded. Go " "bug bcampen@mozilla.com if you weren't intentionally " "spamming ICE candidates, or don't know what that means."); -#ifdef MOZILLA_INTERNAL_API - nr_socket_long_term_violation_time = TimeStamp::Now(); #endif - // TODO(bcampen@mozilla.com): Bug 1013007 Once we have better data on - // normal usage, re-enable this. - // ABORT(R_WOULDBLOCK); } // Take len tokens from both buckets. // (not threadsafe, but no problem since this is only called from STS) burst.getTokens(len); sustained.getTokens(len); }
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing.gypi +++ b/media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing.gypi @@ -194,19 +194,20 @@ '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', ], 'sources': [ 'aecm/aecm_core_neon.c', 'ns/nsx_core_neon.c', ], 'conditions': [ ['OS=="android" or OS=="ios"', { - 'dependencies': [ - '<(gen_core_neon_offsets_gyp):*', - ], + # This also provokes it to try to invoke gypi's in libvpx + #'dependencies': [ + # '<(gen_core_neon_offsets_gyp):*', + #], # # We disable the ASM source, because our gyp->Makefile translator # does not support the build steps to get the asm offsets. 'sources!': [ 'aecm/aecm_core_neon.S', 'ns/nsx_core_neon.S', ], 'include_dirs': [
--- a/mfbt/Casting.h +++ b/mfbt/Casting.h @@ -205,17 +205,17 @@ IsInBounds(const From aFrom) /** * Cast a value of integral type |From| to a value of integral type |To|, * asserting that the cast will be a safe cast per C++ (that is, that |to| is in * the range of values permitted for the type |From|). */ template<typename To, typename From> inline To -SafeCast(const From aFrom) +AssertedCast(const From aFrom) { MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom))); return static_cast<To>(aFrom); } } // namespace mozilla #endif /* mozilla_Casting_h */
--- a/mfbt/decimal/moz-decimal-utils.h +++ b/mfbt/decimal/moz-decimal-utils.h @@ -49,17 +49,17 @@ namespace std { typedef std::string String; double mozToDouble(const String &aStr, bool *valid) { double_conversion::StringToDoubleConverter converter( double_conversion::StringToDoubleConverter::NO_FLAGS, mozilla::UnspecifiedNaN<double>(), mozilla::UnspecifiedNaN<double>(), nullptr, nullptr); const char* str = aStr.c_str(); - int length = mozilla::SafeCast<int>(strlen(str)); + int length = mozilla::AssertedCast<int>(strlen(str)); int processed_char_count; // unused - NO_FLAGS requires the whole string to parse double result = converter.StringToDouble(str, length, &processed_char_count); *valid = mozilla::IsFinite(result); return result; } String mozToString(double aNum) { char buffer[64];
--- a/other-licenses/skia-npapi/SkANP.cpp +++ b/other-licenses/skia-npapi/SkANP.cpp @@ -52,52 +52,52 @@ ANPRectF* SkANP::SetRect(ANPRectF* dst, dst->top = SkScalarToFloat(src.fTop); dst->right = SkScalarToFloat(src.fRight); dst->bottom = SkScalarToFloat(src.fBottom); return dst; } SkBitmap* SkANP::SetBitmap(SkBitmap* dst, const ANPBitmap& src) { SkColorType colorType = kUnknown_SkColorType; - + switch (src.format) { case kRGBA_8888_ANPBitmapFormat: colorType = kRGBA_8888_SkColorType; break; case kRGB_565_ANPBitmapFormat: colorType = kRGB_565_SkColorType; break; default: break; } - + SkImageInfo info = SkImageInfo::Make(src.width, src.height, colorType, kPremul_SkAlphaType); dst->setInfo(info, src.rowBytes); dst->setPixels(src.baseAddr); return dst; } bool SkANP::SetBitmap(ANPBitmap* dst, const SkBitmap& src) { if (!(dst->baseAddr = src.getPixels())) { SkDebugf("SkANP::SetBitmap - getPixels() returned null\n"); return false; } - switch (src.config()) { - case SkBitmap::kARGB_8888_Config: + switch (src.colorType()) { + case SkColorType::kRGBA_8888_SkColorType: dst->format = kRGBA_8888_ANPBitmapFormat; break; - case SkBitmap::kRGB_565_Config: + case SkColorType::kRGB_565_SkColorType: dst->format = kRGB_565_ANPBitmapFormat; break; default: - SkDebugf("SkANP::SetBitmap - unsupported src.config %d\n", src.config()); + SkDebugf("SkANP::SetBitmap - unsupported src.colorType %d\n", src.colorType()); return false; } - + dst->width = src.width(); dst->height = src.height(); dst->rowBytes = src.rowBytes(); return true; } void SkANP::InitEvent(ANPEvent* event, ANPEventType et) { event->inSize = sizeof(ANPEvent);
--- a/rdf/base/nsInMemoryDataSource.cpp +++ b/rdf/base/nsInMemoryDataSource.cpp @@ -154,34 +154,34 @@ struct Entry { Assertion::Assertion(nsIRDFResource* aSource) : mSource(aSource), mNext(nullptr), mRefCnt(0), mHashEntry(true) { - MOZ_COUNT_CTOR(RDF_Assertion); + MOZ_COUNT_CTOR(Assertion); NS_ADDREF(mSource); u.hash.mPropertyHash = PL_NewDHashTable(PL_DHashGetStubOps(), nullptr, sizeof(Entry)); } Assertion::Assertion(nsIRDFResource* aSource, nsIRDFResource* aProperty, nsIRDFNode* aTarget, bool aTruthValue) : mSource(aSource), mNext(nullptr), mRefCnt(0), mHashEntry(false) { - MOZ_COUNT_CTOR(RDF_Assertion); + MOZ_COUNT_CTOR(Assertion); u.as.mProperty = aProperty; u.as.mTarget = aTarget; NS_ADDREF(mSource); NS_ADDREF(u.as.mProperty); NS_ADDREF(u.as.mTarget); @@ -194,17 +194,17 @@ Assertion::~Assertion() { if (mHashEntry && u.hash.mPropertyHash) { PL_DHashTableEnumerate(u.hash.mPropertyHash, DeletePropertyHashEntry, nullptr); PL_DHashTableDestroy(u.hash.mPropertyHash); u.hash.mPropertyHash = nullptr; } - MOZ_COUNT_DTOR(RDF_Assertion); + MOZ_COUNT_DTOR(Assertion); #ifdef DEBUG_REFS --gInstanceCount; fprintf(stdout, "%d - RDF: Assertion\n", gInstanceCount); #endif NS_RELEASE(mSource); if (!mHashEntry) {
--- a/security/manager/ssl/src/NSSErrorsService.cpp +++ b/security/manager/ssl/src/NSSErrorsService.cpp @@ -136,16 +136,17 @@ NSSErrorsService::GetErrorClass(nsresult // Overridable errors. case SEC_ERROR_UNKNOWN_ISSUER: case SEC_ERROR_UNTRUSTED_ISSUER: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: case SEC_ERROR_UNTRUSTED_CERT: case SSL_ERROR_BAD_CERT_DOMAIN: case SEC_ERROR_EXPIRED_CERTIFICATE: case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: + case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: *aErrorClass = ERROR_CLASS_BAD_CERT; break; // Non-overridable errors. default: *aErrorClass = ERROR_CLASS_SSL_PROTOCOL; break; } return NS_OK;
--- a/security/manager/ssl/src/PSMContentListener.cpp +++ b/security/manager/ssl/src/PSMContentListener.cpp @@ -89,17 +89,17 @@ PSMContentDownloader::OnStartRequest(nsI rv = channel->GetContentLength(&contentLength); if (NS_FAILED(rv) || contentLength <= 0) contentLength = kDefaultCertAllocLength; if (contentLength > INT32_MAX) return NS_ERROR_OUT_OF_MEMORY; mBufferOffset = 0; mBufferSize = 0; - mByteData = (char*) nsMemory::Alloc(SafeCast<size_t>(contentLength)); + mByteData = (char*)nsMemory::Alloc(AssertedCast<size_t>(contentLength)); if (!mByteData) return NS_ERROR_OUT_OF_MEMORY; mBufferSize = int32_t(contentLength); return NS_OK; } NS_IMETHODIMP
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp +++ b/security/manager/ssl/src/SSLServerCertVerification.cpp @@ -92,16 +92,17 @@ // we need the event to interrupt the PR_Poll that may waiting for I/O on the // socket for which we are validating the cert. #include "SSLServerCertVerification.h" #include <cstring> #include "pkix/pkixtypes.h" +#include "pkix/pkixnss.h" #include "CertVerifier.h" #include "CryptoTask.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" #include "nsIBadCertListener2.h" #include "nsICertOverrideService.h" #include "nsISiteSecurityService.h" #include "nsNSSComponent.h" @@ -295,19 +296,20 @@ MapCertErrorToProbeValue(PRErrorCode err case SEC_ERROR_UNKNOWN_ISSUER: return 2; case SEC_ERROR_UNTRUSTED_ISSUER: return 4; case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: return 5; case SEC_ERROR_UNTRUSTED_CERT: return 6; case SEC_ERROR_INADEQUATE_KEY_USAGE: return 7; case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: return 8; case SSL_ERROR_BAD_CERT_DOMAIN: return 9; case SEC_ERROR_EXPIRED_CERTIFICATE: return 10; + case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: return 11; } NS_WARNING("Unknown certificate error code. Does MapCertErrorToProbeValue " - "handle everything in PRErrorCodeToOverrideType?"); + "handle everything in DetermineCertOverrideErrors?"); return 0; } SECStatus DetermineCertOverrideErrors(CERTCertificate* cert, const char* hostName, PRTime now, PRErrorCode defaultErrorCodeToReport, /*out*/ uint32_t& collectedErrors, /*out*/ PRErrorCode& errorCodeTrust, @@ -323,16 +325,17 @@ DetermineCertOverrideErrors(CERTCertific // Assumes the error prioritization described in mozilla::pkix's // BuildForward function. Also assumes that CERT_VerifyCertName was only // called if CertVerifier::VerifyCert succeeded. switch (defaultErrorCodeToReport) { case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: case SEC_ERROR_UNKNOWN_ISSUER: + case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: { collectedErrors = nsICertOverrideService::ERROR_UNTRUSTED; errorCodeTrust = defaultErrorCodeToReport; SECCertTimeValidity validity = CERT_CheckCertValidTimes(cert, now, false); if (validity == secCertTimeUndetermined) { PR_SetError(defaultErrorCodeToReport, 0); return SECFailure;
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -633,17 +633,17 @@ nsNSSSocketInfo::SetCertVerificationResu } if (errorCode) { SetCanceled(errorCode, errorMessageType); } if (mPlaintextBytesRead && !errorCode) { Telemetry::Accumulate(Telemetry::SSL_BYTES_BEFORE_CERT_CALLBACK, - SafeCast<uint32_t>(mPlaintextBytesRead)); + AssertedCast<uint32_t>(mPlaintextBytesRead)); } mCertVerificationState = after_cert_verification; } SharedSSLState& nsNSSSocketInfo::SharedState() {
--- a/security/manager/ssl/tests/unit/head_psm.js +++ b/security/manager/ssl/tests/unit/head_psm.js @@ -55,16 +55,17 @@ const SEC_ERROR_POLICY_VALIDATION_FAILED const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157; const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176; const SEC_ERROR_APPLICATION_CALLBACK_ERROR = SEC_ERROR_BASE + 178; const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12; const SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17; const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = MOZILLA_PKIX_ERROR_BASE + 0; +const MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = MOZILLA_PKIX_ERROR_BASE + 1; const MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE = MOZILLA_PKIX_ERROR_BASE + 2; // -16382 // Supported Certificate Usages const certificateUsageSSLClient = 0x0001; const certificateUsageSSLServer = 0x0002; const certificateUsageSSLCA = 0x0008; const certificateUsageEmailSigner = 0x0010; const certificateUsageEmailRecipient = 0x0020;
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js +++ b/security/manager/ssl/tests/unit/test_cert_overrides.js @@ -54,18 +54,19 @@ function check_telemetry() { do_check_eq(histogram.counts[ 0], 0); do_check_eq(histogram.counts[ 2], 7); // SEC_ERROR_UNKNOWN_ISSUER do_check_eq(histogram.counts[ 3], 0); // SEC_ERROR_CA_CERT_INVALID do_check_eq(histogram.counts[ 4], 0); // SEC_ERROR_UNTRUSTED_ISSUER do_check_eq(histogram.counts[ 5], 1); // SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE do_check_eq(histogram.counts[ 6], 0); // SEC_ERROR_UNTRUSTED_CERT do_check_eq(histogram.counts[ 7], 0); // SEC_ERROR_INADEQUATE_KEY_USAGE do_check_eq(histogram.counts[ 8], 2); // SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED - do_check_eq(histogram.counts[ 9], 4); // SSL_ERROR_BAD_CERT_DOMAIN + do_check_eq(histogram.counts[ 9], 5); // SSL_ERROR_BAD_CERT_DOMAIN do_check_eq(histogram.counts[10], 5); // SEC_ERROR_EXPIRED_CERTIFICATE + do_check_eq(histogram.counts[11], 2); // MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY run_next_test(); } function run_test() { add_tls_server_setup("BadCertServer"); let fakeOCSPResponder = new HttpServer(); fakeOCSPResponder.registerPrefixHandler("/", function (request, response) { @@ -116,16 +117,20 @@ function add_simple_tests() { SEC_ERROR_INADEQUATE_KEY_USAGE); // Bug 990603: Apache documentation has recommended generating a self-signed // test certificate with basic constraints: CA:true. For compatibility, this // is a scenario in which an override is allowed. add_cert_override_test("self-signed-end-entity-with-cA-true.example.com", Ci.nsICertOverrideService.ERROR_UNTRUSTED, getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); + + add_cert_override_test("ca-used-as-end-entity.example.com", + Ci.nsICertOverrideService.ERROR_UNTRUSTED, + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY)); } function add_combo_tests() { add_cert_override_test("mismatch-expired.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH | Ci.nsICertOverrideService.ERROR_TIME, getXPCOMStatusFromNSS(SSL_ERROR_BAD_CERT_DOMAIN)); add_cert_override_test("mismatch-untrusted.example.com", @@ -142,29 +147,38 @@ function add_combo_tests() { Ci.nsICertOverrideService.ERROR_TIME, getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); add_cert_override_test("md5signature-expired.example.com", Ci.nsICertOverrideService.ERROR_UNTRUSTED | Ci.nsICertOverrideService.ERROR_TIME, getXPCOMStatusFromNSS( SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED)); + + add_cert_override_test("ca-used-as-end-entity-name-mismatch.example.com", + Ci.nsICertOverrideService.ERROR_MISMATCH | + Ci.nsICertOverrideService.ERROR_UNTRUSTED, + getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY)); } function add_distrust_tests() { // Before we specifically distrust this certificate, it should be trusted. add_connection_test("untrusted.example.com", Cr.NS_OK); add_distrust_override_test("tlsserver/default-ee.der", "untrusted.example.com", getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_CERT)); add_distrust_override_test("tlsserver/other-test-ca.der", "untrustedissuer.example.com", getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_ISSUER)); + + add_distrust_override_test("tlsserver/test-ca.der", + "ca-used-as-end-entity.example.com", + getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_ISSUER)); } function add_distrust_override_test(certFileName, hostName, expectedResult) { let certToDistrust = constructCertFromFile(certFileName); add_test(function () { // Add an entry to the NSS certDB that says to distrust the cert setCertTrust(certToDistrust, "pu,,");
new file mode 100644 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_nsCertType.js @@ -0,0 +1,27 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// While the Netscape certificate type extension is not a standard and has been +// discouraged from use for quite some time, it is still encountered. Thus, we +// handle it slightly differently from other unknown extensions. +// If it is not marked critical, we ignore it. +// If it is marked critical: +// If the basic constraints and extended key usage extensions are also +// present, we ignore it, because they are standardized and should convey the +// same information. +// Otherwise, we reject it with an error indicating an unknown critical +// extension. + +"use strict"; + +function run_test() { + do_get_profile(); + add_tls_server_setup("BadCertServer"); + add_connection_test("nsCertTypeNotCritical.example.com", Cr.NS_OK); + add_connection_test("nsCertTypeCriticalWithExtKeyUsage.example.com", Cr.NS_OK); + add_connection_test("nsCertTypeCritical.example.com", + getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION)); + run_next_test(); +}
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling.js +++ b/security/manager/ssl/tests/unit/test_ocsp_stapling.js @@ -138,28 +138,31 @@ function add_tests(certDB, otherTestCA) // ocsp-stapling-expired-fresh-ca.example.com are handled in // test_ocsp_stapling_expired.js // Check that OCSP responder certificates with key sizes below 1024 bits are // rejected, even when the main certificate chain keys are at least 1024 bits. add_ocsp_test("keysize-ocsp-delegated.example.com", getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE), true); + + add_ocsp_test("revoked-ca-cert-used-as-end-entity.example.com", + getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE), true); } function check_ocsp_stapling_telemetry() { let histogram = Cc["@mozilla.org/base/telemetry;1"] .getService(Ci.nsITelemetry) .getHistogramById("SSL_OCSP_STAPLING") .snapshot(); do_check_eq(histogram.counts[0], 0); // histogram bucket 0 is unused do_check_eq(histogram.counts[1], 5); // 5 connections with a good response do_check_eq(histogram.counts[2], 18); // 18 connections with no stapled resp. do_check_eq(histogram.counts[3], 0); // 0 connections with an expired response - do_check_eq(histogram.counts[4], 20); // 20 connections with bad responses + do_check_eq(histogram.counts[4], 21); // 21 connections with bad responses run_next_test(); } function run_test() { do_get_profile(); let certDB = Cc["@mozilla.org/security/x509certdb;1"] .getService(Ci.nsIX509CertDB);
index 19bbfdcc82a59d8a8e02d70eeaf77f3f5a641ea0..fe22bc496350c880eacd0b1ebb0b44a3f3e3136c GIT binary patch literal 294912 zc%1B=1zZ$e+y8f2LO{BENoi*3ZV*I3N>U|u2`Po8Her>pyG2yIQ8BPv>_kv(L`4w0 zyHNi-yQ?6u#{E7%y#LR8eeZkcn%(PMXU?4X&2g9r3mKk9CgGE_b21aicq>dX28+eG z<M9{_W-t3Ais?p({SoZ(9gF!jdogBrg(R3=7-8xl3{8%Cih7PZnL3R91poj500000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000002^UyZx4u#%D%HaabfNGeQ9Pe>zX z<>rpaOUy`167dT4@d)?9`v-aZMBzC;d~gu{yB}|uKtL>wT!lC}L~wG*NJu1Qd`}_# zLk=8Y?;Na*yak1o)U>dpIPtl8iDOAg<nJkje#n96?OO_hkq1s#N#)yqq~+%3k#fE# z5&R(&uCsR@_5`DHE3Al;mKJup4LKn(gY@0|eMNwmPq>HYaG##h?1~u?>L2J48jTO| ziN;3+`G-XKpj+??4)PuD?-h>s@C*(OH^nC)><@wcv1EU&&~?ra=r08HCwug(J^Iz2 z^A$~EZ;dW&(1k6!utOL2=)wVAB%q5#_QHXa6~bL|azrdSOYU~8IZGSP(w4Kd<1Fnt zO9##}fwN5HER#3|;}(xwKF+!&cb${7B`0S~PR^E`oGm#yTXJ%?<m7C{iL&BES#hGQ zI8j!dC|i!#mgBYMcx^deTdtRrr!6N>J5EY_?vhi3oISNfxa(-FC8xC32pdaIk$J0} zy5^*|;-t3Xq_#%5sksTczgTgW)|{mcXNhjxnzL1F&Q`5C?P9};wc*6tAlzTj*d&sT zm9>=(p?lS`d$mUb>+V&X?p53FRlDw0`|qo^-C?%fVYc02w%uX2-@<IWi(}he9NX^V z*mf7kw!1jC-Nmu(E{<(?aoDR4-Kz=RtBKvKN!_c&?p0FvYV!A0PDkLbCUzGm5iJhU zCIKNPa%$Wj;m}>bwhl%F0bwN{Ei8sBALBF9$fPj|dE{))_c7lG=@`QIRUBLP1clfO zEb|j8fudXk0000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000000000000000000000000095j z7=(zim&vqk80H7&3+4l6JF|t^%sj<B#N5r?%v{S{!Cb_g#hk<}VHPpRGt-y}%xGo^ z)1T?VbYK#gMoev{3R8wD%6P$ez-VV;86OxejAq6u#v#UT#%9J^#tOzF#w^ApMhT;c zF`kjeNMJ-WLKyxG4~7GSz%XKHGgKHd3{eJ_{(=62{(#<2Z=pBSPtgz2chfh~SJRi# z=h3IqC(tSM@$^*s82U(hAl;kpM7N|H(eZR8x+GnQ)<t_odq``iU8S9+9jEPQ?+gF{ z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000000000Q?_f5Ka&)^R_iLmA8_T z!dppB=B%`~T9MEdsf*3BMBa)_5^qI1k+X8DDI<Zmk{-`n89Rova!{Q-nzxcWino#z z$5~lfml}(%U}i?Hjp41ViRP`Wj^eCLb!v{}t(+UlTR9uSS*duc7|vT!2;;5DhjLaX zveH6$E2$%RD=ER8l@e*!Al`~gAaBKaxCjo1rA=!J;M&GvTzkeJwM9Pleq6hMDA(@u zMQxr&v=7%td2?-~7iz~lxp;D|vj^8YxuZ5irrnKeZ@F@9n+s}FX=k0ew#kWW8y!(g zth(dCwVn1{d)p4R@ior2T<c`RwT{-Pjk|iyifdaex%MgnwNZ`J5U!nS!L?J&Q5)es z%8YB{Ou07J1hpX}9vO4(LnE$zV2Ikl`jR22^(*tx<63uJu65Jl`il*?mZ^_g?_Gzq zQ0rcGO_OU|G`RMvI%-|Qoz=M3NtJ6IRZwg1e^i-kk0^2NVMWwhw@*^w+6sBDEtf+r zvPxQ(Yo%nkR#F<ZW}fS$xVBc3Yu8Gk)^I}dV6G*Jb1hK}wR(3Zh;l7!5Z9K7pcY^H zRG4d@2yyLWLDZ_NMdP?ON<bKg6IEA>6l0I4<brGrvx|9*$zldD2Qiu%iy6TTWqLcE zMGv6U>0{_I^a%P0YAbaiHG(QjX{D^93@LhEw7V#)$gT)eSY9}yFrZMr;6_1B!3ySW z=4oaca|GiV<1%9xql#h6(4c>%-=!02S7~Xq5!4UVTa*u!Ta?3;wG_#sH$_*9>Wd<Z zoQpJzgbK3?BML1GRSG^8bQBydSXWR{kj<=NmNQM65;Ou$mb#BxNiCwrP;IG7i~>e@ z(fp!Z+6EenzKvc^*QC8DJYG1Jl1echXng_z000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000090NWt=q@ktIkYlESzML7bdOI4PHm_wq>P=SUI6VX(>Qg2XQgk)I<8 z4NK(bP2lH<N5jVO^N!}{7=?z#p^I34Nn-dpqS3G@2V6cDu_6!<3$E9Y;|(KaB%9@? zrDTzac%Lky87YgLMlQk^q>)qEk$7@W9x3u)M~*}{9Ko+x;rtw7XjmveZwNof2sA8M z2j_=H)CsDHGS@4^@%m?xNjaG$Vp;;36!hyPfoN94`E4kGpJNyr=FiXT$Imeo4fA!v zQLu;&!3rU8y~aH&*gq?QNE)BVE*jn^AOg<`?5=d5|3R!bx^XXlMf2q6@Ib@d`FY*= zIb6{&7fqZy7EvZBBJy0XL@+s(l!GU8(>VX!?Sy9G`13RlKX==sS=jwNjqT6fHfR>s z{C3ldpTqL!xgtMzTky-)jGx2Q7#D>_^awf#p6gKYDIAxU!|oM*`n>V)CpF@?xgn;w zI4oj7&_i^%4z)h9cyczLNXj6QNqT>hSQo8t9dtoJ7v|`~1YH>ND~kc@)khaveKyo{ zc=Q?%ntzg51I=EYUkTLsIaJXw6@Ff2ehwuxO!3$1rSR({@@Q6a{5B-Z&mn_`N%Qkc z@pDL`VG=*@+k=1Z7DuxX<F^4(evUzCn8?qw7XG<g2+cx}-v)5}90E8TMpTSFDM>D9 z$1z)(b<7f`FH?i@gt389z;I^>(<jqk&^zf@XnSZ>nj`fibu%@KYC`FttfRzH#ENzl z5sNem+Y0|F%q}!7XlLGH>M*x4%NQ>UYZ<gcGQ)?mi9UuQ&I~B*q!lx^P`}dLXm9B= z=*Co};4U?{kU%?7q(j|7xkpK$NEM|O?PY#oUSO_f7BO9!3XD69e;C;ed-@mpQTki@ z1NwFPS=ts_F3pnqlDd|fOx2}aqf}GEDFQ_si{gqD3a=ENEvzg|Dby=eALvOQ00000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000002+{{iT`fm3a8Sy;q^V1}4*UjeMw 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 KTe8jD_<sTHrt5G3
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp @@ -38,26 +38,31 @@ const BadCertHost sBadCertHosts[] = { "mismatch-expired.example.com", "mismatch-expired" }, { "mismatch-untrusted.example.com", "mismatch-untrusted" }, { "untrusted-expired.example.com", "untrusted-expired" }, { "md5signature-expired.example.com", "md5signature-expired" }, { "mismatch-untrusted-expired.example.com", "mismatch-untrusted-expired" }, { "inadequatekeyusage.example.com", "inadequatekeyusage" }, { "selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU" }, { "self-signed-end-entity-with-cA-true.example.com", "self-signed-EE-with-cA-true" }, + { "ca-used-as-end-entity.example.com", "ca-used-as-end-entity" }, + { "ca-used-as-end-entity-name-mismatch.example.com", "ca-used-as-end-entity" }, // All of include-subdomains.pinning.example.com is pinned to End Entity // Test Cert with nick localhostAndExampleCom. Any other nick will only // pass pinning when security.cert_pinning.enforcement.level != strict and // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h. { "include-subdomains.pinning.example.com", "localhostAndExampleCom" }, { "good.include-subdomains.pinning.example.com", "localhostAndExampleCom" }, { "bad.include-subdomains.pinning.example.com", "otherIssuerEE" }, { "exclude-subdomains.pinning.example.com", "localhostAndExampleCom" }, { "sub.exclude-subdomains.pinning.example.com", "otherIssuerEE" }, { "test-mode.pinning.example.com", "otherIssuerEE" }, + { "nsCertTypeNotCritical.example.com", "nsCertTypeNotCritical" }, + { "nsCertTypeCriticalWithExtKeyUsage.example.com", "nsCertTypeCriticalWithExtKeyUsage" }, + { "nsCertTypeCritical.example.com", "nsCertTypeCritical" }, { nullptr, nullptr } }; int32_t DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr, uint32_t aSrvNameArrSize, void *aArg) { const BadCertHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp @@ -48,16 +48,17 @@ const OCSPHost sOCSPHosts[] = { "ocsp-stapling-delegated-missing.example.com", ORTDelegatedMissing, "delegatedSigner" }, { "ocsp-stapling-delegated-missing-multiple.example.com", ORTDelegatedMissingMultiple, "delegatedSigner" }, { "ocsp-stapling-delegated-no-extKeyUsage.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerNoExtKeyUsage" }, { "ocsp-stapling-delegated-from-intermediate.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerFromIntermediate" }, { "ocsp-stapling-delegated-keyUsage-crlSigning.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerKeyUsageCrlSigning" }, { "ocsp-stapling-delegated-wrong-extKeyUsage.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerWrongExtKeyUsage" }, { "ocsp-stapling-ancient-valid.example.com", ORTAncientAlmostExpired, nullptr}, { "keysize-ocsp-delegated.example.com", ORTDelegatedIncluded, "badKeysizeDelegatedSigner" }, + { "revoked-ca-cert-used-as-end-entity.example.com", ORTRevoked, "ca-used-as-end-entity" }, { nullptr, ORTNull, nullptr } }; int32_t DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr, uint32_t aSrvNameArrSize, void *aArg) { const OCSPHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
--- a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh +++ b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh @@ -167,16 +167,47 @@ function make_EE { -t ",," \ -m $SERIALNO \ --extAIA \ $COMMON_ARGS \ $EXTRA_ARGS SERIALNO=$(($SERIALNO + 1)) } +function make_EE_with_nsCertType { + NICKNAME="${1}" + SUBJECT="${2}" + CA="${3}" + SUBJECT_ALT_NAME="${4}" + NS_CERT_TYPE_CRITICAL="${5}" + EXTRA_ARGS="${6}" + # This adds the Netscape certificate type extension with the "sslServer" + # bit asserted. Its criticality depends on if "y" or "n" was passed as + # an argument to this function. + CERT_RESPONSES="n\n\ny\n1\n8\n$NS_CERT_TYPE_CRITICAL\n" + + cert_already_exists $NICKNAME + if [ $ALREADY_EXISTS -eq 1 ]; then + echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)" + return + fi + + echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \ + -n $NICKNAME \ + -s "$SUBJECT" \ + -8 $SUBJECT_ALT_NAME \ + -c $CA \ + -t ",," \ + -m $SERIALNO \ + -5 \ + $COMMON_ARGS \ + $EXTRA_ARGS + SERIALNO=$(($SERIALNO + 1)) +} + function make_delegated { CERT_RESPONSES="n\n\ny\n" NICKNAME="${1}" SUBJECT="${2}" CA="${3}" EXTRA_ARGS="${4}" cert_already_exists $NICKNAME @@ -243,12 +274,17 @@ make_EE selfsigned-inadequateEKU 'CN=Sel make_delegated delegatedSigner 'CN=Test Delegated Responder' testCA "--extKeyUsage ocspResponder" make_delegated invalidDelegatedSignerNoExtKeyUsage 'CN=Test Invalid Delegated Responder No extKeyUsage' testCA make_delegated invalidDelegatedSignerFromIntermediate 'CN=Test Invalid Delegated Responder From Intermediate' testINT "--extKeyUsage ocspResponder" make_delegated invalidDelegatedSignerKeyUsageCrlSigning 'CN=Test Invalid Delegated Responder keyUsage crlSigning' testCA "--keyUsage crlSigning" make_delegated invalidDelegatedSignerWrongExtKeyUsage 'CN=Test Invalid Delegated Responder Wrong extKeyUsage' testCA "--extKeyUsage codeSigning" make_INT self-signed-EE-with-cA-true 'CN=Test Self-signed End-entity with CA true' unused "-x -8 self-signed-end-entity-with-cA-true.example.com" +make_INT ca-used-as-end-entity 'CN=Test Intermediate used as End-Entity' testCA "-8 ca-used-as-end-entity.example.com" make_delegated badKeysizeDelegatedSigner 'CN=Bad Keysize Delegated Responder' testCA "--extKeyUsage ocspResponder -g 1008" +make_EE_with_nsCertType nsCertTypeCritical 'CN=nsCertType Critical' testCA "localhost,*.example.com" "y" +make_EE_with_nsCertType nsCertTypeNotCritical 'CN=nsCertType Not Critical' testCA "localhost,*.example.com" "n" +make_EE_with_nsCertType nsCertTypeCriticalWithExtKeyUsage 'CN=nsCertType Critical With extKeyUsage' testCA "localhost,*.example.com" "y" "--extKeyUsage serverAuth" + cleanup
index e33f19881080a4208713fb8c4739e16c8d498eed..9d6453d125c50d86bf079d3ecd276263fa697309 GIT binary patch literal 458752 zc%1CrcUV(f_b>W{-h1x^M6d=37?7?M3sM9`dP0-lds##QDIzuyD|SEy#e&$eHx#fV ziXx(diijwP3O8Z%{-SKoOy1}Ian8MGe4b~ox%A6ed+d>IW=KL@>a;i{K2R+vDmH== zucnX7!r}2a3pF(y4p&G2M+C>jL;r{Ck6+_)tkvOWzU3Cie&P61y>RIw*p0MXX|y!+ z)Sszdskhmv2LJ#700000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000002^|CMlFURh;jJZ*YBg&H0h5g1RI zPl>1eXXTscY;Eact>$8Bwb)wiKfkM{9TK4C=)Az*(s{YsBJ1U9u1gj;xmv3^EK!^1 zu*7EZf_W}#&eo2LE$3O!Pz#9<jL`jO(bJjD$0IvW8IKEz3<yk)iwURyt}i7aK5FdO zzW=$tukL@&_^o(&WM$Ryo?~C&A4UlYh>VMKj15Vo#0T<@H@9HPd~0{Lv5?UM^9$6p zDY`^G9SiQUYb+bPMmU8U7|#3>p1-a!7XIrR`Z_jTJhF=Fc;A28PN0Sc`o}ZBg!`{+ zjK`C7qN_8HlSfwmKmRo$ad8QOvCOaG`s*_Pr2o1KSyyMZA)Z%OT^+x6&e%=HjG1Q~ zf8A_<UV!xtXKCf&>@q`*LZtsgzuhQ$^ndgj=VQOY_#IuwZ;=_lBr|?V9{VNZOUNX~ z!GLivWE|!&4rIo`h;g7W4pjQVXzWtNe~x2UB<hVF|M~Zj#*PMKN5iq>oUtQ$>}WK0 zq>LS@V@Ln7#{OyVpZ><q_5L{@yRzQcmG#E1tT%RLy|F9nja^x9?8^FMRr+I9`eRl4 zV^#WNRfc1!;aF-omKu(whX15v*EAfv=A5ywB>!_9o8Z`gsUGp4b4IP+Shpl1o!(f> z<ELY@9{Xzjv9H!2`)U&LpRfMs3;+2Y{jnoy>}W7{Wc<5HWB*pt*uRxDcDopi)f$Y| z8W8{a9Y(Expn*O~-$0jns>eM2;|oa4Qv>FyA@g(&^OXEQr-sZjLuQ#Fv&@iLX84~n zLuNaM%ytZ!?HDrKF=V!5$ZW@u*^VKz9r~#e^OV9or7}<bnWq8F(?I5F(Epr{{g3d^ zDV5m{mC;Us0fiVq9h*3rIEFc2Ln9r14jx%s_5anKAM;PCuP*bPlYSS?#Z5opv7d~) zYvF$u000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000007|sHbM#l^rL3xV;uGa`+{|29at;Y zg5AO{VW+WLtP0zWZNb)K`4|mL!D6rwjDju4oUjF$C1!*Xv8k8}CXESUoSDO!Uozik zzNB9W000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000Kor2_&B-n;@{f>HOEfc+VnISC!!Bq zL<wUjx9)|gkDW9q#i)&)>^Tyw$~eKTTXsNY>}1W{d&*-cdA*WKV<*`wgA~V3Xkv2} zcsV)o=|y+s8EcC5Svkg<pzJ2gSYzfI%P`h(@pfs(x+1+%im?Wizm{aIeg}*s7^~OQ zC*q9N{ceF6V|AVHA<9^t9N&vDR{IN?!i?1}*G!19&aZC}WULnDPXrjNsf!UmV<j)R z#K%}k9l5-WmAFrshq2DIs^n&@+N=Dz7^`Mi7AIp>%lW{;!^z37r06C<pOz+OkHdap zgIGWI344!qV(nNf_7H2rZe!Q52J9?Wht*<-u?lPtwgW4}HezeBd@LKouv9Doi^jsR zAdG@}VaqUQ`h@@h00000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000004`1(UXyXqU>pdHgF54& z#yF@l4l0a;GUK4cI4Cj>3XFq1;~>X4$TAKxjDs}eAjLRHG7b`qgE-?L#yE&F4kC<$ zFykP^I0!Nh0*nJc<G{x_@G=fOi~~30z{NOlG7cP^oH%{~dfS?r9XRX<_66(4I<Qu( z1-pe^!cJqgSQWM#+k&mf@-Z5gg2iAV7zJC7IbjPhOUww<#dI)rOdb=*_%VFukIXNb z-Si6q000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000Qf%>FQ){4Kh8sQeAO6V3FE8!_^LL( zs*bNJ<E!%csx-bTj;{*itNi#XH@?b_uQKDS^!O?@zDka-6634*_$oHOijJ=$<E!xa zDm1<dj;{jaEC2Y)H@@<YuRP-`_xQ>+zH*MQ96X#7{EX|+N2HnAfx~`aU$Abh18c=v zuv^$A>@-%3RbjiaE!cW2AERL@SPT||QLyEh6Se@e#EdXqOb1iP<S}uKAH!$<$o!Jo zO}`KT000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000fd4a%|9$tG<EzH_N*G_&$5*xSRdsw- z8DEvhSEcb)aeP%6U**SFx$#wYe3cnrrN>vP@l|qsl^9>e$5*lORdjq68DE9RSE2D$ zaC{XQU-`#ZzVVfJeB~KmxyM(o@s)FY<>29z;AdQiJ|fNZ3|vN1`f$dK49SeD3?Gaa z%f_5D3o;+2AHm*d3TL!sT+WQi7|7g<HDNn4wbE~9x?^gXQHFJ9FIJaciiKsK%QR2V z$XJ)&@xS}#1^@s600000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000;QzDaI7#@f%1h#V3so~zALLlL zl3tB`|FPt^CL_2I@IfuDeFo`l!0Yhb_BZjt9cob;jKET3j@v17Y!A<(9jlz@g`GcA z<2poO1bMntKm867_1-Jj30k|1vL4td>{Vw3U$X8xRhn$RyY|#M@j9aJ)$m<AKB_SS zu{G}ssy<UI61FH9wJg9THHcfesWJkY+84VPxnA1($o`f{-HDCcla;MhRTzQP=X{m& zOQO58bI)oX3^fwGk#TRgG9y_3YH#mGiyx}JH#NC-oN6t2Xjr^hi4kaab2YwcY`1!= zXSc;`nTl4Y^v}hLjDX|C&Bj)Xx-&oeGR-bd4_kwGi>y*$1U)Mcg_C}BXstcAZ_2h8 zF7wuj(gfrgfl{=t+=cTFXErb28<?i$Gw)M%F^3!@2zhI5pJ1h7RDT<9Z}xn~9{inH zJy}LT&Acw7R^i!pv`s%VeyEklJZrc>h7m;ne00$3n#%!84FiSms=GQia_lIOW(1U0 zh0_(WiwivDYsp!8_uiMhPF^R)2v*&3T&Q02TmM(jv70yL?;mUqSpQy<5p?EUl9n`e z=AR*yEnN9pQJpFl{X&8f<Qq(@eA*;@|1w@}#Z!6eaQ>p+4Dqpl`K6fOjk;G;YnKHd zb&s-scj;coAu&cUcStZL>4rmenwPqW_-*=?<!mxU89~-jXZ6OojW*dPs+gw20)d}; zL8n9*!9zzLnH<S{-NF`y5%1EnkhQI=aKenh?2DmiNs>*LX_QZ!=8;;DSs9<Rgc!kw znOBt-DwltpCBywKT|O+lod1BgAS0L>`y})d&#oCcJh9%~4=p!;&8(>wU<8Gi1d8gL z-s+hYB%U#smnP=8$4Bxrf;I1D?a%b(PjQWG#J^l28FBM?2aS&r==!*7sqr5!{<*)Y zqMow)i@vD%LtaL3rLVZ<WoGe}BLhbMG(S!a+zP=a9!4<T{hlkaBmRT!35i6dMAtOU z-#?ylGlG>P8Zs&N3Lke5nl>*@v$}y>%u~t52u@fZE}G@_yUfIC{z;XL*A^!}UtY<{ z2xxcOvhRmVAFy<t^+dPQhq5uSbv}nICyC!aT<7OS^*f3c0qWY@l01j5S?3<-rvEYI zzy=t98bz?-e@*}Z00000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000000000000fd5MfuHvO11!(gHa5#LC zgE0;pz_wtinGKn{F_X*@%o9`2e3@yJIW;39V@XDL#-U8UjN2J&(!ZuZOjk(znO>Ui zo_;btDcvY-R~jw#Mrvi+yfp4KYMN&1+f@71St;=;t|@&f$5KU8T2eM94=1-Jt0!|L z?@0DdK9`)CY>`xvl$Urf@kr9bB%!2`r0I#D5?vAv64Da95(X1aC(0x|P1u@%kAEGn z9nT-XFFq*#N_<YdZCp)UaqOemx;Upe$++k^z1V?R&sdX~l`;M?BQXuJ$}ulvcE|8V zcSlc)7KlC=9U6T*x*&RC)ZwT#k&TguqZUUAMTJMri2NA2Byv_nWW<t)-iVq=k%)T{ zYs34(o5RJz`oh<RJBA+(j}9k>Z3v4EJr=qlOfR%Q%sEUV^nNHQR4T+JgcR~HWK+mM z$cd2n6^~cc1%D5IydrUh;fm4~ZYyMhlY`xZ%7T)E<$|6DlY@qW>Vp&lhXaiR6#~x$ zrUpI>+#2W+a4ulG|8xIy0bT)00qFrI{=fWv{LQGDR3GXH^@6_&wVk??ile-us8Mi~ zT@*h`10{=M;kU<crSE0mJ$_bx9DY<kg70hJ`M#Py0Y3A6-uUeE<?^}elkNT1`?@!e z*IVygZyWCe-a+14UU^<CJS#o(ytF;vd)ayMdEWG#<|*K@&|{j%ZI1$v9*=__q01YW zA9nxf-ncw``HbajmM>l|<R0n1WZBwfk?tbP?zzue*1N1`nV4Ij8_`Y7?WkL{TeI6b zH%HfFt{YtLyBu?Mc9n3Ab=7m}cX4qcEsbC5vUFhS2^XoQ50`Fo{_gzPS;pzRbE&hN zbDeXdv!PR&Q?g^dW0@1#amdNtNzU=9qp_ocgNK8$!!w7i4#N&-98$5zSbgTI%)ks{ zhD6$nw2P?)so^PeQsk4~BwbHjpBR^5mY^2*A?|K$S!`;Ib<EVL4^fSgMUjyavm?a9 zTEdQn#)Z0sNU!)Fd@8ssXnBxvz|Vl^{yY3LspeEQzYf34zN>r#e5UyDc-`=-^bGM_ z=pnfLqkFac+GP&Qh^}8;@4IYtiC=24RL1FvQ@vw~qsRa5KdJx#000000000000000 z0000000000000000000000000000000000000000031%0(~=`DB-qzKFgD&-S2xHo zfNDsl|H?q$P~X4^Uxdub_%*1UYJe|9)>H8X$a)e!A6ZYq=b>^#B7XH`xuGwSL03-? zpNq_i_#72Z^RYZEFy%k70kb?C6%+BRCR{y$8bH-IU`}@>GAH9{sGL6CEMz?uk0I+x z_)KIy1)qV+>C;W0EZ1j_Ck>es@u`20Z!FehmZzX%B0l-As}q9)NCCRKdd%r2A#*Z5 z5tY-Yn}DpR;^UF^BzzpQo`R1><@D*sOqLUw<B3M*M10hr<NGHj1~JPcQ85u8G2!a| zM*e|@{><ryBXcr743*QT8;Y!_;zN-2B>W0wJp~_(%IVV$nk=U<#}kOmiTHrO$2S%m zG0Xi?F%eIlcy*$#{~Qu?x)fwi#`~di`gDDf^;EnMvYv$ZM%GjCUZ|WtUC+t#KmKTY zAaf#q`GoN?VqIprJ1QpPmrb}jMUN6hzwQ2Kx^BpvjCV!l^y#`F>#6vq$a)gq8Cg%k zJE3y=bR8$l$;|OMAaf#q$=~A}i{~)Q?NKoizxeN~6OD)lb0|j4=`KR%Wc)%@PM_`q zWIYvchpZ>zZISg9ybUU+PuF^~{EzX>N9IKQyuZfxPyEM24l7hl#9L0dI@w>J8lcOZ zt_3nD<IPbyeY$4IdMe%&Sx>^7AnPgkxu~2zUE@h||39`RBV<m*lm8yySRBYKpM#2t zc*BWTCkF=V8ZxJAfXvBw5-O)pS07nV#p@yKNqAjkJq1rh<@D*!o-8LZ$1@9=6Y(=A zjE@oj@qlgyDkkEmPrN#btY`SgJ;O9)PR8q?a{6?&k@ZykRAfB~uZ66q;HRK+`gAoX z%m27%&_L!yJYmB481WzX4C<(uh*z6<b^4<{{Xph)RgpOvuY$_y(^W>+Q}Ig3dJ<j{ zSx><$pmO?j<tNMind6Z|=0v>ggz+(ADzjV$6%+B&6RtkTpB&)-$8@ETIT<gB%IVXU zK-N?7;>dauUJO}J!Hc4D`gBDm%m0|JFfu3Nh5jDjSp3Iy1yL~(FEH`yMkE93AJgSW z=43n{DyL7E7g<lm^C0U<cy44p1<!@b>C@$$EdOJ=9LSuA$4?j^BmQH$I8;o;jZVC} zfo^~S^L`gMg3QUd->95wfcu54r{aDh>q)p_WIYA<1C<*RaYK{k%==y3ATlT7zE2n* zBWB+3;=Z9`B5q*f)phl$`T@-8ensYFTt6zOPxlM5o{H;3){}6bk@XZ@FDj=`_tRuK z^M5|vM`TXKeV8ylM$G(18rOr0iMZ|wS2y(6)eU4m-o?E~=49MER8F7nTVy>I*M+Pn z;oczYDY#BlPM_}UN%8>Z<6T?_GAH6*{XM?1nE7}Y_YxHoaqSbYPM+gWe>D9+(|v)= z$++jJoIc$)WIYx43|UXYwIb^&xTmO`KHVpi<pIp`JVxe3+@lHOW5mq6ZQMgtOvF8y zcy*FdP$2X9UEF<SPR6yMa{6?ek@Zwu6SAI!yN9f&;O?Sw`g9v7%bDK};O-!EBJTEt z@iAiNlR&szsF;YmIq~X5ilIL98A#j>WKPCiN9FYCUPIPXaaWP`B-|BbJq33emD8tt zX|nu}JI)4VPQ+cDFg`}id<GJC0TmN*=O<j<z(0T-z<iDjcMh48ac5CEeY$6m^;Fzx zWIYL2kF2NQPN8!8bn7O|na`2oP9k$6?!@2Y8;k#V$Z;GM6LH5TUY*SN0_JpUkvSQ6 z6qVDbdjwfe#nm9|Nw{icJq33dmD8ttXtMl|#|#INIT2SiVSJ4EkN@>6Q85u$G4bjK zB$B}&(>;L9$+-QfoIc%s$a*SnFS4G5+k>p9;C7>O`gC_qmj5x`a%4`#?VK<^M*PQg zcc5Y-Zu`Wm>lzRPsm$qaL*``MR#Z-(?iOS{6<3C=C*d|D>nXTWR8F66$z=H-cbuD$ zIT5#U!uS~RA9pMpP%#m=e!|sB0n|Y9ACGs}A#*aW7?snfyB1kb#T6mzNw_u0dJ3)( zmD8tNFiAe=kH@?D$ef7F`+Iz2@gI+OSEFJgE_dS9$z(&)AJff2=44zpDyL6(6|$a+ zTZybE;b_Qu3NA~9(-Qx`$_<IwB(XkoxS7b9n4!#Rj{jqJjFdUrbW}=An|NswmF%zo zKZ8w0#^e-KOf^VGR#TIZ)ucpZH6;NR8xrFui<vJq#vx;3?1Z5)QsztaF{qRnJ@L{+ zqo6+?fkh!>awICI8blzgso}_KQW&zD5{ilqi6N84%zMZc$e0*BVQ7q$`M@g(l@bFd zTv|WCfb_?=3<1cP?2n4622^A<m4d7$`5~(*zNpxc=rdW&+|s>~G12Sqp^c@?4bKym z5<MnfnoKhCXTDQmIWi`@qhhMTGGsN?4OvZcMOIT>P_ZF#>0~kUyHjUmOmvzsG)Bt2 z*KkCoM2Cr&CI#w}nNQa)LB?czR7^EkjI5?ELROO&BC9D2P_ZG=ZnBv9XV(@P6Ky68 zjgc~+rL{(-#Q761O{9=?iT^XwdB~V-g^H;LmdI+V1+toCj;yAbp<+X#=_E0cxoMjq zW8&NiLt~`OXUB|DDbZ-crS$?xdKBij0c2!Mo`Z_128PILssXZ^L_$_m^ii=PQE#%C zd3UIbjETg*hc=cn_qN%nlsIeRrO9M{L*}~=W+G$q3{*@tn2xNbPD56cbdc2)ZB%SX zoH|)d`k$$2A!Fi{2}5I~%x4)jQ7KVl;-yJ}24v>rQUWq2tD|D7ff}-!s*0>8sUWK< z%Ba|os5Dv3++h@vF;QW{&=@K6aj85iCCW{_G?8o&$b79<78#ReP%+g&8d*)1LROO` zk<}ClRBT8TpDboR+b@QUiJ}vR#z>jZ$%vp*qVR-E>-y_b|9F8`2pN+FQ8Cp(09j4t zM^=;gkku4kRBTA(nJi}R_}s{t$o2Qo#!}{v&xuNj9D<z6{Lz%SxTL7q0AU;scNO1+ z!^$&%W-iM(mLZ(JBJE0=N@_~V!<5;{1xas{tP*!53?(d$uZic63y8fKs}PeA-4Z=B zDlf7#(kx<2_&~Tr*ugOFP`{9KA#y9?g6{?E1Z4-l3^WcX@$d6rM6IB5QhfYQ`$_pm z`P}xI;+^Hy=4I%)!K2q>!Sa3XqwXHdPA(I33v<2hs_v4pv~?-Tx!9@4$=Y$3!%qjd zCACY0>{l$lx>$8l>cU40i3<wt-r8B(Znqh<S!#X6T3~+QyoPy-R*9A^mNP6?o4+<U zGuvwV&D7E4kO|LRit#yPS)*9;U9!%c9K%<Ja}7#KUr3AfEA%<^ymjk!rHIk9@66Vk zMVr|+(_lvN^zP~NrtQ!f(pjoqt<5`?qIFhFdP<b$ElmxL48l`_zIu_`J2gwyZ7SbX z9F(h+IhDK>>lGyxBIIw#tIMUyK9Zd+lP}#VZ7NkJ`Bl<hqCx^M?kRRsOjI;P<cf&0 zaI(+?p_ziK1v&)g@|W;^=3BtKm*+Q+JNI#JVXk1#OPq=v3G^G{{~a2EHlN#ar3CJe z|8*w3Izyyo5xsc$r{&t^4hrMBMTibWOXe?yh$JpGZ4;lZpX#1DUoQADSa(OTvi<(T z`>*TgzHiiXnsTmqZ$Aef&w*DX3h?m#Q^vvn=Q)>}<vag0@8(UnXA9~tJ+P?OMMSQ= z%7f~ZkuR1cTx6=1P499ZvP&w7jV>?VHW3!^J#=)oxl31(@y*=n++`gu*0B;cO`ZJP zx5lvS()?=1RvDVc;@!1^MLPZjvwk9t^z4y<NW;mhxxd%X`9SZ|3O!2-8(n%I3Vz$@ zxZS9&?3K9Ce#+njZLPxgdM(29-US}!**Hs9UCLZ4ot88zYLPRToe;}+Wa-JHBK=<% zbziu0*Eg#uruhWDOG{*zhD0{HTzlgL$!zYA5^2j70uQV7f@z++?!C^_D;j9GJ*e)9 zb7$4%)Nk4Oi+#D{q(<{+q-L7m)}*N)ua4z-(rE8Gw5es(j^3rkq+K$<6KA7K+)J_e zuy+1Eu0KRKM1_mQA3fUGc>0js`JpKZ%i5OCGGf)GP}|WcMWwXU+f`=hiw(W|Ss~bJ z_2Ssl%7sE%_OIHv@zA?8M|a82EVmsXpRv}?w0Nwp9<uwvwoQF&-mSZ`26M1%*!=cx z$H)m*T~@B{D@>R=Ct{Xt#jckpTBbg`du4I{m5Ze}cm#SPZ*VWBsng83&Bwc(^c74t znpEGyTd)D!j_=5!++ORJ|L)f*>#p!H1&RLR>h2d`Y?iQU^8UdC+ayzs1OnqN?)!Kz zh)=wwRKCOaie}ZNa?jR1DTnDznj)KIXOe=ymRxVNtsyRN?6lS5Ee~tGEc!kuc&ilO z*IT?l$q&L=H95<E&U}j!@%iNVQIorE0x5I##C?y+mEU}wAw7LMbsnC6dz(zWy(hey z%SM;AyUr?Hn5q+(6s`Nf<XY$IRS!)MJM9gQ$kpcPSaE!*0jn;rZuVPQ)GzPN$@gnY zi{r}mUn!l(4lgO7jvDXrENg6kL+^4fx=S(}T`szLi7QI{T#51_D~pwLRD4RNY<eo^ z{k-Sqqtmi0US~$I>asbcX}50tI+vc6*LEp1&}i}8d8>L_SI?B_=eV!*uJt3mOJii0 zR0DSY?3|rzeDB=_8tJ};nEiqu3(my8$+JtOEwcUXTA(IXdW%(;`Ay<KzOT6+sImTR zqMXAeE{A5xk<z&_tHW2WUSxXdZXCT!BlIlU*&pw|N<EkH>14=V8SlGum!){t)gD<X zu+YO+x5uNrX|B>6R$Ue(HGZP@=lSo7D#+ZG-oGJhUzAO)UYB9pl>9@*9y8qNU6RqW zWM_Z8#`Ri2s@ZlCm7CMaIRLY{&|_vM^SXNb5C6{5$nIy`S#`NeKty@n{pzTh5w0u! zN}h4;;hsy;&J&ywtW6$lvOA<f?{W@$mh9}09A*0jyMnY`=G&><>o=}mqu$4t{Yymc ztbUfsr#a>~(ph!+)=8;%p~idzjl-V7Mz(jJl5DCfvS!O`Yu~QQ2vq4$q<3kE?2@hh z@s;#~#}9YbIJs^gEIOi7<a&7Y+?#-M;l2aw)ZPyF4Y7QzVbs2&vUsYC)uBP9lHNTp z{pWXXkoOFp89%K^yWvi6lr6nWgGsw2|2KE)+xI_Soh~)aZr+)YqkH@h4>igisp7U6 zQ7Xnub1g_Jc4pP(9no{kHl1Fd5`NXnK0$m`e(64m4Y-U2oB10qn0i`%_(kuMgzl1^ zS)N)GR}<_ab*IZ$yTwAOXl8e<Xv;0_t##jKW40e9&vUW-GtPfMsNekBmZ{=La#UvP zN$)<>vUB;5qR6&0v#)Mx>pnx@dh{o3Jrmx8!bX#dw+>V^lY6;Notb6-QOxk%7Kbd+ zF1rrX?H@kxk0C@iv1-zEeMeoRp6jwHKHX+k={Eg7HxFd!1o3Q2%#-5&P_Mn5-lQI~ zNp>c=`0M2@d=`QJud46dpP%(GtJO8-3QlHY2BvX~+&MFO8>=Q`E)FKHvE@;_KBL&+ zp!Bc4_4_~7y7Y(MUN9=qtM+U44EpV@`>)&k@Au=db3bH~dFk8Xf$2N?Uj^(Y-+0q; z_0SjGl-;698PolP6oR^0bt$FtxoOvok7C=uT7Aqolj|#dS%Ub}aP~*raJ&0$;n{-p zE{W(a+1Vev)1Sv(vy+`W`}0A<`>tDqv-i8*dHFUo?c5_muSk-tEUPXX3(^`Z`V8Ne zZGGaSz0Ir9c7<V#%E4CNE<56mvTM_6^e$&3yJTyBd{KLvc%V|tB|j{4ZxiXcxA7Z= zP{oQp%6_-`60JVfY-QD@_mh2ta%uh#L(G-eNZj#!Bw;mLwOr8uYE=OZ*Yl`|Lho`G zdY0_$kDlvn2fb}ADwbj8X)d2l!!FJ|NmadcvvOnV7ST<r#)GW7jCejv@#*<JzXQ1o z)Yf1Ae1L!IMWIxZc6+5Tzug(}sTjS>ndn)vvp>3#UfwC(a3hrG$E;S-CxtF|)v_yo z9`O{ENLnJ%XQ;Y@RhJ`0DrfVi6iO}`ymLHx2%oD^+<adtX^YQ8>zxV#@>V<PUCuzy zlAZnWSL?DL^KYu{uNSV6Y+lu)y=UE1o%azm*<;uAID|i*vSQUGVQpXD@1u(k?cG!! z&VjpQ<NxG%?YS<?6Iv%vDiJ)?F44Q3j_i`H{qZ`n&CYVMqKZcOY^$eVa}t-V()igM zruISBU~hXxi=qXqE@zw>R#M=WD1P4cHr!cG*6n!I;jgbh${Xx$m?gQhUH=}v%W0E# zN&Ro`)Mws}Kg-=4eDD2AshA$k!+0C-BS}SXs`W+r2|Ee<$`7&Xa_32do11k!$~e2H z**@KB_~CKyozF8>@~+Q%^i=)9_10W^mpbS!*_mb9))tQw$IGL5XBxlO_#m=4&?@%( z3(Dq^3r#W44B8%7vFdUfSL|g!YoD7jca2JT$}c4uS&-Bl-oDfoFWlO2q-DARee2Pl zxb^(~E<!e%+?(IuM9xoMdi`}<0XacdQBos&wagO@^&h6@#z)Ngzq4xcT3A}XPR=r^ zE+vlb)@x_&8K!CIs0z%Uy)U-VtC+Ln0KLhn$R^pDq-EH9kI|K<)uxYL*S`E?_)cWv z*==V&o>Y2!FgP}*AgP#Dlf`RBbIM%XKH)x!jBGx?>GP%XuXxGGcXhW^21YxFwL9py zx7NRI@4w$s$wrs!t?qunpCu;<{_^dZ@pVo3;Z5a!{S^(EXxl|;|8f%nR$a>5@51yK zy2~_o9p*DO;7y|BrDW~)Y>w7QnA?&+n6{AK<rH+6?Cg&}1okVQ=ur{cv^&}F%=ex< zGnB8?xWBqS<<$w<joW^S?`GBInq~6@HV*F<s7^8-XbZV$<)EZJ^6^xD$qL+-Q$eQp z_tU%7M0Uy6{-`t8h2!X(BMyt*azzuK&gu0mQd0`<?;jP^Cu$AE=g(u+Wx|_cWxxJ` zHF~N$N1Tfs_{64vx+~&a_w4xjb#s&&4y~kjsezs)JNx4~?X6YR#>Zke=3G4Dx@Q~y zq<g;XrI}0gb;@>TQu;<lSamsDKE(Uke#Iej;g$&1%R;eroC(z(<+(QwUQm>=aepR4 z?~;I?B|H10)2OYyfXuGus4B_82OGH~%6)%@S8_|eezSZRl|Nw4V^&?t)&))5Z76ze z`;*wgoM}GuUu-?|BXnWFZI={d-D88**XUiUqi4y^{>W><5x1~O+DoafGwQh{Zf^X! z1E(IXU+yz{ZHpf{@a0@qUFPt8SK}5@jed99^5WOX`5!~g53CZIzwVKOFu6BD#QGS$ zOEqMdZ0(P4qWWJyN>G~9`VpUP6#2>|W%zpIDCx}Sb>8)=8dav9th(&nxUh0X*ixr= zd-IR){jwlV=#*qY!{&J%QXTWZspvjTrgy12X_x=i{&=(|a)0sDtJj_^yKnl2_h3#y z(A}4h;*|;ycZz(xCf(i2^3mm|y3*FmQ-&<2r0m0Vy6zTb?|=Mpb9d^n6j4yZG$)AO zr3$)Bc4oO$>=&2A-W~5(z8#)_cVB)3X{*WGcD_Lkmv^V@=i2htv+7bm?Cs6G2TxM` zpUEp@)AP$a%`GqAcs3%ow54}*)}5>#`qra7aqIc}ec<dok>z;r^hn0`(t}c*`AgQ{ z<o)=oaw&1U`N_k=gC$+bn^-<|Iryae#ILR4v*YDBjxOjKso=Ps`+IG2;8oKt61G#n z54+KuR6;h%&LocyU!K*hQ(kAc$a<*DO!{D5j(5J)(x-~2aXU!ccQ3xps!7^QjuOeA z3iF%voBP{SZ07RCyxaR(df^+5lnaEU*{w(Ex3}WIZtuU}6VFDME?;)_y|NYGKl@p6 z)r$wE-S(B6jFfSB8-3}hX&!|=ZLGR1cQxpEyfH)9u()%V+xkmUkLtN3uI_k#Rbghs zr;M9g^7Jkh&|R{#Kl+RNTdoQ}^h166!)ejcqhG9w@tabOoi1lA|G4|ATES_Se|9W9 zzqmAsJv<;a+jXk--2z_i@9mve7vYcnzRdGEbn`oUm-5Ii+1ei?&dRMh;!<0YWu~H) zo^xf^SKiU5KQ;1fPj|a+r8<soWYy)52GwTi$d`O1xyuzvqYgj3^^ShK5fKrdx8UnD z#XvpAGX`?#S+cV~F4`Cx^Sw7Z=}7zZd6cZV8TBV`o><sp7u0<&tS4X=Wq?(eQ}#VL zSY~6k>4)C?*A%n0+13)umYZ^M>qc=BSNrE5VmwZlMbDC*{ZUD$J4CS2b=}u{^^IzJ z0qR^euZ;4sA&vI7ZM?7CUzxJ%l2~{-V4ma6__wX!m1ZuZdg46z<ZoPd-(BbO)cxt` zdd900GU!>dvp>2NS4?qz^fUKm{>7gn5gjX{4+YJRf0I}&D{+$R==&pSth!vTH+Qh{ zRoqNVjiM2G^<T81g7anlMWfdr@2fO7w@BJb?@}7sC0qMrt<+kM7Xk~)n(_-ulUL2W ztGdf+@zEDMcosaH>bb6C)_qo89xB<@`uNs2%Z83;wJocKnoAP3)+EiiAESBq<l85D zd~@hsN=@43fAykSceTE)M4ZYgzZ2cfSG#S`uD<7T#CGTOqkO3^?{!`*c*v^Dy$glo z4w=^v7~0SA+PzHl+)J~?=S{Ddo)c0z>3*vp<ED2hiSCk}S(b5SzneKwwV(fCy6@~m z>*xOXSrSO}v?)u@JL9uued|t^&tz2@pY!1AyMAfCYUPTR)0W-q5a}ze<=WYNp?#KC z^y0bntw&<w*7NsI^03h)UZ7D>NOz8v*y3*<ALJ_Xrk>X|D34S8!IO34xn^w0c~(t2 zsi~!iiFJmZ8aZ0M__o`Xp7Nhwg@M}@I=<bt*v=DijNYU;vPrgHe^iWFHH~yFHgbwj z+?V`Ui)&_E_Fhe|nH&6K=jsxRV_R6h&huL$mYR9o{NnzIS6|bLJo0#!{&q3_`1<|P z@9o6V1S<XZ7Mpl`Pxz@OcD{eyxNn^m^$5x4sDzH4VVSbfs?c3JvsU$SX2<c_j)uKt z)uqNSQdi{;ts|E2FG$rq_77|is(TvRle>27?-twVC-$AEcPWbQlAZl=KUUw}b;5Sn z&N}6_Ip<fJym+E`a@ETN+vY#XZZdE+z0RsjUw-`awsKD%&!0(7HBz<T<NLNs9#YT{ z|4wq|ZF=H1Lhn)p*(F>1qrbuZ5ofM<uLSwMf6jVrv1QTPixu(hFO4}nYD`svG)q{% z8kHDdq^tadPyECBeLi^`cDT$fi>%B)_j6s(Ws^AuF;D1S3ZrMq&i<&%`$K=jcfY(q zk40p4xkyrI^94J+N*V52y;D@^&zL-x-{o2iP;aVMyDRyB%yJ}gJUp@Wc36ADW#a)u z{{XT)VK==?A@nTS*&lP0r=*#g9KO@OXMg(o4^s~ko^KJHE;u*MdDDv34RJqzv+DAm zshLZ`YTN;nU1di(k6YP!9(TQV>}>4S-p;lwQP-M#>0Jt<XUWd~_(|h@uT8&g>w<>M zdONHulg?{s_(or!EzWsM=X~B;4^dWKZfd>O^ENWMYfEkCAss&j$9oAHi5s4lTjT2j zU%fc*R7vkr0NEv5`=ioSeXS$f?VIZ4P7~xa??f-d>F2Cl@<FdL@ArsXmn<i%F3-hv zg((|Ucg<U2<gmiwdeXBIWvdLiyM-#tBDcjxbuwOm<e#j|KYsp@jV_&oou3gd8tY!? z*|*UtcBN#`eTSBMjVQ6&mQ38$%=?P0y7bKZoo?GtOOC{O`MS?@Q}KWOtE)k*K3M3A zrH&cNkC)yhAG%9+X1UOExBt1DkIGL)m#*pF$g}=nM_Fv*@>h;0>@SpgO?wr<s>`LC z#Mqyg2HQSl>}reuwQjkgQRLZ6p;{y{_qrs}2l%`6t%rBQ)-&N}G1+Lc?I!t+?%FSd zKhGV1u&rd&bm9D**#mt!2gC56zHc}_SjVc#2UfibIgR%==baTZt_diX_TC%3uE01? zcip1?-b+@)LG&hhkWI2P$=mxBb{d{}d0(+>WnXZd(Wkjip)vl=Pb?%|DtfBY%&DxJ ze5BzVxq|lk<Fj(nv)YB%)@+*@B~YRJXj28BuIknc{rdFVoBLn4_uoI4%0`z9<R$OV zn6+9XxT5dk(%+?p-7_|wFiUs)meV3=Z(-B#$EwQ@mFJ)5$(-RWXw*|XSi55W;giE7 zyeaz6R24Qqx_9Py8NEv`beHVxkGG#Ss2nwEcUd}rg~L3xzJs2svmCEK`W=BUS%2=t zzQY*H?{XznYPU5}3v`AHEJiq1Tu7_WbCWeay(FniS#@1)%MyB*oX9TO+8-&+mp<9{ z|GYCzjwh|(sWV+;rRo8$=9s?rRA=(afhlB`U0%JP=<)Gw>(;GBS+}ecXqHX70=vzM z(vOMga^`eiFratIfu1Eh`{N$*-hG0^F9Hv~S#H{*C$n8bVQN#y_L;w)3DVSNq;A(` z`MvnbIAf=WK~`TL-PR5#cO)s+;@1h<=Nj+Zw^QMze32TxOFVj(?Cg(M6iVvVHr}-7 zdTP^IYW#a&v*2u_1@X(D+OBv&QWY;{`Mm$ugewW!Di3ZQ`0baGS(*^hMp#hvFbW?z zqfW8=6?cpoy-OT=mh9}076Uta4V)^>b!HtrI^f(WC+4HI_}rtW9nY7h%4xk(D`eHB zt$q5d+t%eJ1%+Q`Sv_!(!JpU9&a#$vK3aD(U|Zc0AsPWgHp$lZ=(=-Quv9{GhpK7z z$=TPWdcBPoo_Nqj68a$gWx40-)hyqFcmJqMLs9e1)Tgzk^&fWZi8uS2(3&~etZ(;2 zcQEgCgaD0@Ia!nX|JCj|t#7m^J8q-bo^;#YHv8Ol3VE9EJ)$JH(U!>H;0YaK`A(+; z&mC=bZ;yQ2sOQIV|5&XrVTIqop7N519eL%6Uz6@WqY*ODO|mn|)2c~#Y}dE`C_OGr z(zQ?gZ1%-cICSeLlja{U1<z%P9c0zyna9<h7d1s!6GA&$Hk*rN%iL4r+-8`y{$-W= zYAue-7ij9~6Stkef0&+)9u3X5B{dy!KZE`DKW-)G*Kf&tu~*W2{;l(ef_a6-UL0f9 zV^wAB9<z6Mr#GikXVo^C1|&-ljyB0yuf{8f=DNGe`_t6ZkUg?9$Mj9+Q%)ugzBGv) zUP-Zd#9MYthcA1cP^F^N>W(jjWF=NTQram}9y8SobruAAI7sDqW>;oa2e&nA9cj&S zczH`|1C5aSuN(XC4;EmfNj;-?*7njur_OwR8Ml|htw}nRwvN}zxO&KB=BaP^=M}7) z)Z+5ldDWy%r{aaje3i}eU5A&6`*wA{-*4O*rr0H$-9RIxpqpf8V_YeQeVTf3)w#4V z!%u^zn}tOePP;4Uu_14mGq@~eSL-5HO@2Mp&q?+m_E~veaOH_xWqM@UzUbRGdp2h! z2wzCzdC@~7BqN(-YhS!6SbPJgIJFW_dh=uBvixs6HhUAjbJud4-i~$IWkEg5s>#nM zRyk%k$nCgW?P%CCLb|rW(`TxD$MmGoZ+dbZkxqMPge3GN+1VC*oH?3JJtfRH)o(U^ zS^KW^ly#aCRdSv9h|cdz87B@0vTE|u4&i%-H#*w2b&e10ORiB{bos&25{KvOU+p>I z8u^m&gGNY1Pm-NoQEE!K5g|s+VYKJ<E8ef0>-VkV9@ec{wzO)w{qnM-IYq3RO#O0Z zNLOmZyaV%+H8)WNIvk%Y2`%@}HKpVw4e-9Gr_l%r=t;7(DaKL#%R{p)JLZ|#`P#h^ z+P{gamZ5SvyEw5j`i*4bGgnqkDx{o0|NCMlx5ewS;b<Ld=Sf0g!ig39_ltMk8rqWd zdn1hyk8F~yO>yt}1B7RL_(WCK-PKP%?4fkdDUf>g%IHvr?xQ`jri=7hzPd9~c>mz? zj<>&ko22=5Uf%mOU+q@;_SaqO#nL>k&LsuX2yv4(sr%pDm-Eo-L*#Pr_4sTs3;7l} zYwgApRy(^I7o6wbFRyAOcd3l!TgBuz$e49`CJcFG?JfGaamGjQj)B>A(L5t_b{=p3 z^6nCi5Q}b-ok_kkap-OR#$hw-M8M(ig^y?();GU#37FFCemr-^gUWW6ubM=sKPmYz z@7ALA=iBBjD$}tI_YCo|j0{<`aqm6x6Gh5dH1(K?8_wS!hr&jWg^BXL-*?Wkaanld z<)KTddzXtZyW`fQ{VUMz#ns-c$}3s*XtU{=ZU<jMeUa7?|MLR5mjxER-S>92xU6<& z=8uMczAH5KXk?G<%+YSg!6H5$qX$1XKWN3pSlYd9alF5WNBy<@#a4WE^xf60dR&a( zXz_m7=}W%wYMSxfU6sZD9(}%xeJm2Z_P?GYc&wL3i2B!!{r5-1u+b!G>20gCvvg{s zFFC82Iazrxkxx%_RPPgT>v~4r^X9f7t0ud*#JIS{solu!#v+raO#LA6>~Tf?t<_T` z>!zC4eN2_75hBq|va=})9PR#cK;4|O^>KmWNb(au>*dzE>rZXmyvX{!foYk_GgeLR z{_ebT#t6ox-Wjl9u=GT=Xz(f$mxce%6k=5{CaZjkMu<Q*$=0S=e0kpInD>%i2RCW$ zd~<4gOhZNq&$=N&u4_FTUSv9MV);V7@FvRzqu=Gs)@B(9N-ijyT{8WUm+SUp!3U$3 z=WczRSVbd*qbJGErYNNw^geT&DwlKQI>ILzrKs+zH-=W$U+W#G4fS-tdCBtK$y|gu zowKfGE?X`y?won?_Jf2Inz*XUx$>9RtUT9SsuMvYgrO(N&ZbyfzVKG*+j-k_2HuJJ z9UF~}u-EX+S>tj4%$B5uL$gNiu)G7hGIV5??yQ>ha|;`IH*GH6dbz&nRL^78SS`^4 zhtR;MG(sqPlI(1XTW<LV)+{^px^~HLf#Hnn?wZE-Z>l+8ikQA2WVb~BsASdT`Ga>( z3`K^<t>1D`|9CooS5wxKeYjW0bAQ>aIS~G|)ssdDK{m<Ors&nyJ~J;bvbFV0y%L!k zBa!Q({9x7Loat-I?_HWvSk}k#&6Vq}Czqw18a4AUEB2h%yyg1p{l99z*UtY)nNesq z!uY0p#iULCS1*pe4|}jsuqxiq+tNPh+^E5__8;Bh68!Ei=D%wxPeK%iST$*Fve#<E z(e`%r*)ODm_Qj>jG^szW-nn*W{_F1+(~i%pp%H@7O|mn|>Aw6r8pXX)Q4%Q!K8NEY z6Q+4;uKH1V{!~Gl>pidMEMKJm(L(y%h?g@DcxAFdfwn$%QTEBl`RkT6?y+0A?6om* zI!!%j;)e702U@Ywqk*&T;X2C^8^4>CrjmIZx}UxcI~_ZHwq-`s^^ldbd00NWFv&ei zo%tMJwrgqnKIdopKkc<Y?Gp{3JKPp4uchHV?I2A(5ZNO;b6j`-#fZCftpDdXDeby# zH~qH~@5`sZsmPjpd)jubckL{H(u8mNhSdRGmD>lJ*H&BXCzLnT&%@``q;yhnDc6@b zPFqeR1WdfKCw#aU8%@5{o$@a2M6;UKj@mt`x3qR_R%o*-UbpjzX33>1N%L%TSv4s! zWmTz$<m-+31EnYL)x?Gi6~8%VmDXjmcB3-k)yvRy8o?jkBs-g8gV3kxlv*sU(z|Q- zcdv5wlHpf*NBOr7UAU%nDn4eT9m`w$tEPpJNb9`%`j38Zu4FI#KA5d}W8fN9WtEzZ zL(s_p8i9&zlC4c~#-*f=oSK-HdAVm#6Hf)?iIyDQ7I^TQj#r<^;c0E9EPoC&v_<{* z-c9Ojmt5pfdzElYMD+9?-Hz-<@-%}7_sLgR9HkK`=t;7(DZba7y7R4Dh@jockU6tz zK59k??+wb#{aW*@s-!#MxOfe#CWASSZ{_6QY#(-bZ(HYX>CLIei{6J|y~<9p&d(1w zmwu!X{LqtRXH$&&=_jx@2Rn;X40$|cHh=z*pO<_~Q`~giGTERxXM|b48{^jP`xoc= z3w(BzyWQa%D>AY$c+Re*VFf4b7-sh6i<=FN;ESFlJDXz7(VdbfFSg8SE7rc;ShLbX zWyu<$$WLxlzL3oDHdU7sS^i*zOp(x%?`=11rPD88R4$xu=AfT+TBtqdNNVrQqAgK! zG=dMZNwzk{1HPwhE0jm>_^8pWmL|5hS#5L5k?+n9SYdr;=CNS+9ju!45$pT?c}eAk zW4#^Qs$%`U?~ZUMRYz3?%`Saam}dM<ghue5w8{VK9hH?WnI3izmu;@&+-qF>=7=@F z`iQc|G0*%8O0;P-_wffTf4H%-vq3KI#upi}op~PZI|Xq|_a~;*lsv>{imgmHJUO36 z@Ip7q&LnG`cCI{`SghvgxYe?7?fmjv<<8Z4TS@P~bX*UxA-5c7`L@r+*EN*i)JH8c zwjM|mvu?OCG?yH1c1Pgs-O`BYvxDnt>YfueoCzOE$VQLDIw!tXk`E;hCG2#+D_h-E zIr~>_*h|Og$ky9on@^rqVEIlA62ZxSX=bW_7)fmX##3#z>5a8@a#WrEu9JO3{PPEB z>K@1**_os8^NQWNA1sE7@<f+B|J|-OC5&s+*{QENiZ@a!r?_dPu<G%pK51>6nMnnu zN?ylKw?V;W@fn`|Hl<&37i*RsulCcX5tje!#{T=`8QEx3GWLgztly0_-!9HQZLIl$ za<O!iWn<3ZDUC~~g1KEhS$=;r<IRcB_vZx1HoO&{QTM&=(T(O;&w@_*NbhTi>-M^P z+nGjiM>omNrkGn&eSP}Q5dGzP)nUI%<3*1xtvltj(e<D|`TC{w+u3HUn%uw7WZ>5u zmE0QZ4I3Pi{mddvR-M)H+s%1bShoIs-}I|A!ZKu&Y;B53E=@<?@{@mj>N@Fvgu2_{ zUHPZD9z);opd)%*1tC3;ST%Wg@D=Wms_i-QXkWq&_qz|c4t0Ht+?RiC@JrtT9>u~B zG=dv?lI(1Xq@a88SG}e#etd=_NvHG`SH{}WcNK!1_@807oa^*UZ?I~T<U*9=H7*vc z;QO>|Z-zv+-=j4IvpXhAVO0;g77<qq(Fm^SNwTvk2EAx*A2H<oVdj1Ono88^e77Da z?#i00qlF#cjJ@WGUt!gxX2sJ_UKdB!MHrU!&CXjV^?W`tvPp-$WzE~}j|HZy2WSKr z^d#BY6itKPUA7tw-G5@SF8_;Wv1cv!^?!}5&$Px>*2}F;$l1)Q$?G@slnvs93L}OK zdYeQNWc?qJ0&*{JuFj|^*<X2Oh8B&m6xk$Oo1#kS+6Ynq1MWI;H|BMzR>sv9ls(st z`M9%kr(ErMJ295e10|nVwpLs`AQrgS&E%u=bh6twkIkX)K7P6t|Iy>zaY-v0!Fkdq z_5Pdt@|h~~+bx|w#*J$HJa#PRxnq%mw2+vNb6@6>(9AbSDZ;Fp9R6K(=*G^4va?#s zZ|;9otE5~!r}Wy!1re(wLoBKXNscsv6S_%uCg~Q>ow5GDd2Yms<w48RgYcO<%$ldY znZEZj?cwU4{W(djn$&)u$(8K*db_z;jAwFH*mJXl#b)^YMMZ-@yMEf7b}68#J5Jni z{{B!|HhN6h!sW10=Gpw?iBm=O(oesvdG?DKQn7E_^s6bqKhL|>%<@M=mwH+S%ptx~ zI`(95_x*PZWpZBI50C7*5c-&B&?3jPpQi4B?2(;0hMgXAEL564o1^%5s&wvm-l&gi zVOQ7N2AQ5p*2`8LZfDiw$9|W+(m}>D*I)M=UQ3T(qjqnt5RY=jsyzdhe!DM(_t6MT z{&i#j{Q<k|y!S%imGbUhlHCsJ7_+IJwyuhUKLzGiVKp0hH>T>?82PelGNifaq2u{o zIWLY#n|c>To=rJ$qj}rT%ap|DI&)?Q^6aA#?9olKvnj3{Ss*lPej8=YJk8{0?(78; z?>-$#-P3etPW2^sEt${zST%W(Jd~?6$LTZo*@heMSGoE8da`Ym<cIj{63(k^vftuP z(Flu?O|rEqihA=ZsropCYim_yJ*+o9{ClPMLOaV_Z!QTw3KV|3iscKRLsWT@^4ehQ zP>N|!ae?aG^t6NvJr#qz(v{ntclK!7(+G>ulVoR840wu{Ru_J|&RXz=bjkUW->qwp zc}SV9_)yrAbYpXkP6DeYSAKa$t6vy?()4^o<2Sn?liNvGk`rP|x5^Oi?9Ee&tECYZ zq9@7Drs%p_;iKsA)x8Pcf+Frk74Z@SPk1`2g)VC<ljAyN(qmb^T(^y;+m~g>k;l1X zhsf1Tmrae7EKa>&-7_u(ms-n;MbZch(350mQ#{fgr@lz?Yn!FIeO3`^)(_Le;_F&Y zy_?>rpDMrGZXknIlLJv-ZJl@hmT<e9p{Y4@XTVcY)r}8JEU>97Ryv>3T^~my*dd!_ zYg3fD|3Sz1MDW)cub<v&Px{V%OKbKXN&RQvA1pX;=vFG?!t#BnANCy(+GTdSC{^|4 zPGMiq6*8sQEKUxjd{DVz+gnvMLL=Bt+T?%rtRE$^TBz^q-mdF+G!2h0uoH3k)Gu@E z^UtLc)#^WwK7Q)Js>uyDFD|95Jz-jQ;EG$cZ(L}G9j@^4!)N+}%~GG58e(K<1RHdd z>`YPtcdh*QlMRjB+<ET{jk$L>B{kiAtL3QG=e)Air#YSFGiuda`oBGqJ=>5Y<}l0m z-1^8GLBScBZ<FJrylx&aNj@n~Q@5VD;r#uv;B53r&Zx{&9?p{6F}OjJto+#XwW;Gx z#Ue+KtK6Tj7hkJp`I995{$&G;nm^kQcYb-W3cqF`GeFbE`bU>Hw~b<Y=2_V^n)-ZX zkL=8`;K;+5io9JJ#irZWWGx~%F48{td!5hG!pwryt;r6OGg$Rlb7ETeRg?X>al53Z z9MW*+tR5QWOA%dFa(TndnMZ~@-_Z#F`l!kYACb;Rle-Lpo}Y9u-~6a%;msggr`d+M zbqQrXIc>W<cCAUvlVbT%Tq$*TO@zN3=8FjsUjOB#diL3%sSlUld>Wc?W7Q0k!n{Em z;a{IrIpKra*=X`mX;I{~df)jK7rL!3q%AseP2M(a;hYS+xenXUmyf2~vug5qfs%Oa zGqnT<xl^0G3@;b`;vd23AHRH?Tl;0t>lw*=X@q}$Q00V=i)W+BkhnF476(7ceXuum zk0&qZez;GX>U%~~ApTAQKfhWl%O@|Ede`ro<+9M_WI<PB-0K~K<Xdgj`?q4w+`ab8 z?iXLR3ytuv	d7q4jJu`EkvZH@W7-D;vD~=DhvTqo{2?n&lR;I6pu13Z8HAbC&NH zkaw5VJ;j~s5!1N*^Sw-JzsX)LjKAAwp+R)zW==sla~k1aA5%Huqx0Ela`h9+8u852 zx<#g{fpQ^x%2shSS%1Ff$q~2V(P-ukC399ys`PW1bic(FOgq7s^nA22gJa|ot%zgS z_aP;T@YS}`(`f`V^d#BY6gPyQjz7(BTAz3PoPg4_W$_7(+XYf?QjQI)N!KS{oy+p` z0PF7mqWHRhuQ<F-viZ39;JxbUK8LM`b9DN{_v$(>`RPj|m?E2GYg4SB`s}&t8TTdj zB9E&)qE0V}*yx}sQpUBmvL)N>jrguLteQMlJ<IK~$0v*XH?4kdl5c+1f3I<?qqQAJ z<h*J#@8^X$8sT4`QaRx-Ghn01<aOe|p7ae-6t`w?TeK{&H+@=i=l;&qK7o8Q;}wVU zYFRbeHa&7{c<XVm0jm$s?eoqJ^KqQyIlb-qxi6yCnyZ|p=?~QY^&yoL{z?UQo~#nx zSjeM$CR2aY)qy=nay?3D2XY22dwiOH?Bv=uwevE|kDSmxMof5n!gE2iT<U>`PlRJl z5)Uug^MgF~b=~r{)NNmA>c$f{oWK8K2R3@tzo~V8eTv?ynbUjAK8?0`o{#N}7VSKt zQsQ5AIycU~kyVcwX9m-M?R;`Ghj?g5%I8+W=aif}3!&}i7fbBo=5BAOps5=ndt_&h z#HhW^gzEz-jcczynD)SH$FVz#hoZtvX8Jc|+vw-^>#^!lGR#G)+avmE)cH>y)lVDJ zg6eTuF=-d0pZRRM(4u$y6OHh%&!?R5mt3&XWRCR8o-e7i_S6^YZ9=xz7qYS}7a8tI z>U`hcVY;&{m*rcHcb>i;coFxcbg;y3XO4w#-`%z?qZY-Mg;E}eWU=ns?`ed8eLUra zziNYxCYQ(Tow1!$*H>y!wB+8vUPXbS&~;`9A3ILbHfq}UT%&<iljdS+7>D*yaX{4x z_l@@#-g~K<T(f2HYRv`NVV}2-9YZw2zdoIE!e98oMw733Iu`zxoOaZGcYAKvDPHmJ zjk7jaS{ArH+ZN67WQjA&*AxZTeJ4LG>ASX6nh;*<RW`D^Au{lm*T{jR&mz9+FRG}b z5&rezloS5i5jL9K7Eo>~{QGC>my!bVhe&}Kz5H*|kJePyc~2M0e)n63<(nEVnEPIS z7|}pp6|RzcRAKr11B)a5oU~S1_J1IhnjO2UM<e{}vneP1<tOaCe&a52CM?yY)~sJv ztNg}|K_zOZ^058I-WM0{369idv3#S=YbE9HYYcI3C3|t}&a@bHRqnVPF<|rTyI=fO z^F}AHJQ_hCJxO*p#aa==kjB$qM{gZFbf{^u#KStxE4+daciJv`(z=>oDf<GeCSRO9 zn7rn!%9Ue#i+>f`G#$*lStxhA#x3pViu?ms_9lWff*!I-_I{#2B4NW+wL!T?`F(?a zl=wHC=0B4Jd_CF~SGSkkR{75IDgC?hPTU>Wh6Tkn#13*TSiePG-~Z$1`877}_D<QR zw2p}XhqAMNi}LIGHr;}BgN!stx6<8`(%oIs-AIEdARW?;h@_MV3L;(7(j|hV@Q(NM z{=)D*xCZ}%^H>|_bM3v?K35RvhME#$91RqZF+TX~Z&(QImUs;ua%s6QqBKj+nWvdi zFQ(tn2S58N0Qe*$<OtoF_7y%4g-9`a5OPzj7q>T0hl@sziVN(x7S&P^=!TgRawrZI zot>{XT*nX!t8kPwpMLh<<(gw6cjR4TPiAjHlHWVt0qiZWTm0@dEB3bS<!;)2-HQ5Q z_wv+8cjs`}v>xPF$u!bcK~$^|fdes`2bxyD%GD(bbuMpL%w^b_80%D6vOPOQ7NwAk znRj!iPmq5BzLXCzj46dL?U_896zUy-js7^L%wN%4+<zXbGx}u8Tt0}3<yJ<hY;Z%k z)E4cRwh#Zxw<ItAF{*xaFDDb>(8MN0<gGtJA>d+T6>?3Vee$V$(N$YtMNVZf=GPMH zrnVoZm{D9?lAg~V1iB%mgcwr<1*GerIBXW=RVp`+(M8zz$UE8=<2A|Xm%N9w$73AE z8v}r+K(zrn_r>)mezcrbeUkm0T5bWiQuyQ`#jJ~h^;eI*RuJfhlM-TB5)_b`X-Mc@ z?PD`>J6?E2VO)$SM1N<_EgL_dIm}VCYZn6Mjfo@`%hIpe#X<MuMmSxKa^z?33$~~A zgcl*dj)EfW#)3dMl#~!7o1lO+MVZ6v%$HOb>#np`Jy>x@WY)vYl_4@3zP!LvDCGxy zUWdQ_o;o${TO`NHPYMPbLq!wNJFk@9y&yBnO*1%JNy#A44I?GQ;3y~{MTXM%Lo$*W zIeAK$6Ixbq&rYO=-fbWZY?VDOcL*8;yc!uX(V>6c<0R!BFDn*WB0N4(l-!QjZO!zU zmFJ7t2bYW>&<!Ca#P}*GARP*&55v82M9d6?7BrfM%9SQ|l4Zs_H?VO6i&%GO$N`&; zGo*i{FY7!lTBpu_Jolv>M`4~n>cuOG{(}dwX7fEnK_L3uD+x^~E)Rc{N6~R2PJ77q zWxvl_ebv#1JVqjI4{bI_Wjk5{6|nns&XS69f=2vHIC&hSgtC8AGs-j~@;;e^RF)p5 zyhCFUi0)QMs6sKIM4jXwkAfy)(w{uooCkubReMjHYpzP{mS-6?J|+V`MW&=N95RFP z^0{I>%Tb63*KU`TOVTZ+WMT~H5O$@aE(!wO@KHhx6oUeCr)a`abgytFdg$3p4iKx+ zN#9wV7<2X)YEX-1!SiQ;9Rpz*#7uK72CXE1FWX;qGMi)n-k}zhP!au+PCpQ7_V+vp zbVEl8G1d$U$c>`N<f_~_!}~HYk<@s~^>RZ#lI@Mpn0({yYknOi0RB#HtFUx(HNXa- z_Mbl0=6w?=#W|94X}Gax{Nv!WSe`356saKt=lXCpC>W)_XtGFhqCRIgdh%>}=*^{< zRDurCQvb)liSu2K%_M-&>#(x$ym&w1itilR4Bc1qT34tHQ286tnN{8OW~hLSXE=!J z;jN6&)KSNDj-xXtMtP(89!YudUIMqsJFnQlH*>mb@kn)Fm2-d?MX?^cPhq1D(<aqg zh&+BvaA;U$LVQdw%Is-O2IC@G6$H9rqJ$VU2h9btl$P+ZKc&4Vx(yfX*vNg?zQu$L zJ0AI0N;Cy1Itze}Hl&~QvUi+wO0}ZDvZJpD)I<k*D#Ovfkp86LDDUJFydMO*A)<sB zcLxRJ-~RRRb>0WPaWAB>sittdL~=vPkYAaAB)<N!sPLQzJS^A#k?_9kmBVp8t}n^N zxEsu_??9ZzLzYp}^<GD@@67^1pc@`ah@pE>Ku))!<-wSpZ=6OACgd=NF7iI~&~@ty z&5&%IlklnfvkL^7%*A)GRQB=x_phbGjdYFttI9`c!^3OhsLIv(zA33vL7*ENN{G>a zP(V7?DfYkL$VKIkQHUg2*`QY;@BMIcVuPX8n+|W}bPsTULlCYdViIcg+p8`%k@)+w zmV1}4)PMOb<A_Lyl_q_pdJqJ<VWET=KnMk76gn0?D~}5tKC`0w(0T_GskPLzO&bGK zb4SF3@n%dNAV?v`dW)VwwFcz{hs&(jlf?;t4`29^!0j+SC9)zMEw~B-k>6fPXhJbq ze@x1-cKHu!ZLz`JYbt8PtwF^ZMMk0_kdhTi%nU3Lq?ih4fU69S&sCFK_N*j}($;c` zeR*s@o@U0RY@w;u^B@q}t&mWK;<=Vr_}0FfVQ;3h^i>kh!;*I*hOCMoel0kd<2xf5 z1I|lQKeu-~7+llT^;qZ;Q5hl3t&Bf0^L~1ipJ;USPV0~<2y{b22{9593dsA0b2_}4 zW4a#x-^F}QWx2f;2}7TId(*}dtl86KFoXa>?qjU)7iai6hmM#3ol2YACMQ9<l4MlM z=_BP~wc?&E4FcV8P(lo*gaXo;2E8cR&R~q_fZ<TqlFzi`qI3U283qG8H-#@&mI&Zw zylG=M{y+>OwQ_@Y7iwK`j3Cc1nzFXf%+h`mnV`b@{U9o0$iTTi9uo@2$-$x0n$cdp z5g}g$#WGpU%5n!Eo7XL8Z-pyUoA6!(io=UqhyJE^s1y{}_BS@?smykT!?B3vcPU$7 z*LH*tegZQR-O30}9WQY75{or#aX8)am{I!fS#~y`EY=2icN~XwS>Ve*I|F=6ISRY6 zU-_AFTITa3ZyQP-JVgDkj8v4)>-plOwnya%L7*G@Nr+LQP(YG*4w=;HRtj(NWLKm$ z)w`qkvUSeRz*>DCpu^Vr-2%9E(2X)bjr-B;kRMRW&(#CNp+>+t#=qJ|^EdfX1SjRQ z#URiP`y|A`QYaudLutzwPhVL+S5#H9-3izf*%BZe$yRCX(94|n<nsW$uETiFlE~QF z5-Kp)_Ols7@*`4`#|X2lTY2C5Pm-5jY@0!#8}dnrv8hl%9;b28CLn%hsr?ekiC-U) zyhvK(tfHWwmMS@D_;_S_2MChoSzA=X)g`azP`;*mU(<YnpWm~m;ukF@del8ITbm6* zpd0Q<h~cf!G}`zaQfQCQdM4y<7qktPAck_Cp8DDzX=BkMvlt*!0!lB)>-;UA;r9Ri zrdeN}#lfL$DpGZB!-*UrSyHy(_(Bo93A>@5gcu<U%~4eoN1dX{h*jJB7iO;`4yU(! zvta(LaxRO3%D;_|5McvBPVXo2Xm=Wkx)j4wknl76>BqvvB))0;^qE>{aOTl!P!I^` z_DVt%iefvYmELcD;8WK&VK&OA)0VYl1`N(k!xKCqqA1-w1C+&hZO$e0JzG~d6xXt) zzlsDE`tQSp3?2~=xAA;IJ^Ar82!wqrBvhgJosE;Li)FFd#jMyGr9#YLgZ-grolbBI zUM%UVH`Op;a(Bc^x!Ge4Z;=*>We#&+=}#;m31M0-?;Eq9K39CM4qomX=1GX5zR-ML zL`cT!j71+_jmJg2Lwr(_7Vr*j0@m8*D~{vor~RflAV}<&Fj4y*rOcg9m+DW3MO>mH z%-e{b3TtIu4NuzgkADsV-4IVgj24ChlIY&xmpDWCd(^pd#-^gmoJKiGmi+w<2suyv zol`?>0cB7>=;<dfv^)KNx<294lATr{3zOmfXyCME$&`^Fqxc~=hzb)jaIOzHhGxq> zkS!av>GsQ!fZuldv(SuLrvcHEDmn+-r(f<<FY^GZ5?!9o^s|%AHaOrcjq>4QHnGd< zj`(Y%^Y9*&Jx)G1bO@rlcPk?_b(G)?8C1)vJ~Mk`?V8-ld{n}xp-4b<9;6~kf0m_- z3iwE<v-RBub0L!Z=-Z^VS0>MK8wzHf27&@|x7sTEjqQYEgFrW&lMut6q4@z)qu_o+ z-CSQ#_(Xp@6ytYb=aYLE1daJ^mv6!=IZdhoJInKXaHM?pMM@nJWEzhfyiI;nAZ*2b zw{lvg{TX(~#~lQ^p`3&mISmD*;!@_b^=4`XQDbM33-NK*qazF3&yv3=L?k0HL=6%E z6({*KKOM!dei_H>i>^ypaV<&pCpPxsvd$IBCzbp(Uj--k4dW!l;A?35)0AfmK0M3+ zZNNm<ER)E)YC@}g`gf~2c>5`1#@i3sM}YQ_r(3B}wLRFI^L43~f2cHZHc@)Im`*r@ zVjm8PV$FI7fo=#VA;xb*0a@OYjiWe&BRjT;zwOsDI;O?-yJ)b1PjA~@+ojjnr33Kv z!Vm4famm}0c#`QS;f>-L+I!PVvOj`8ilKxh)QaqZf68z8CLxA^LjlRZXOYOxC~bcr z2`{3kP8@O?r!Y(F`qmnrd0(PY)kOgak~{1NW4aRww*6a`*XV*tWC7(r%R<+uqHI<L z{HJ|=#)3e|w^tIHP_$)?<(Z^F64Mi`wdiBjN}Gn?jR=z*%JTW}L%IRk9Po&xl`iP@ zsvz4`6lYlLu3E?u<=^80>7P%h>a`1v-!)n+1c8ulg@h^;@t*{SX82C$Evv<M;PB9> zN|aq@rqo{fGQn>>>3aPc(0e=3kIwfx6`l=C>{;M(k{Ul#s@Z)8G8rnz?#N}YwO7tT zpc}eLh_UR@Tr*V|*loAB!Ag+IzVs!|F+-|^$IihU#}N}{HyL#$bOzkc=AFQC^~P$` znM;4(c;r7I)(87?PUWp^XovdFmpp6AAkYolB*budC?Iv8cE@F|^>nmS4>^8n(sUX% zFkLwbT^4HdlVlaMH3xhq^rw|qzJa}r{4LeAT$KL<yjG_R^l{C|s;dvYs)HGLa6we? z5P<_Rq8<vy6RZkrwR`n87RobKIsI~Y-V?=Ar&WqcvI3<!>WNW+yV;i0X0n=m#gV75 za#BSPh+n=qXl;0E!ZA)TMo!CBlNlC71$QeWRC8T4f{^}NkC{-f^2Uj{|5+LgTz>8& z62R*+LX}pS<OMj7ud~i<RzezO(?^-24B^)o6DkEKBh`<e#+%@4QJ4vk`V3(a$+?11 zoQz#vJse%kTo1fjc2W7T#bri}3P&^+R<Y-q*q*S_BccC)Dhxzecs61Lynzt`wn2<B zW}Vn$J$0`)`xH*o|4haR_aDP^?kY3QQk(|-l1KX#fPD`J2Br(P7A8Q)zuq6y&)T=! zm%_)>d)S-VE8KIzQ^4b;`>wm}^8&X^H!as%R}2>m=a0@LPHv8ajtmZ=_H*`pc1gC| zwo*1Z)@Ro0R+Uz$mSz@h7DVPQW?#){O#@A)O}LEXj5drU46_VQ3{>^Y^^x?9benYX zbR4w%w5hfHpG`jF(2UVo)euonS36WwQY}$MP%(Jg_!LLkPU(}<14VCzQ3V$HD7h6m zA=y-!0~rPBx6&|DI+FF0m=f0F-QpBto}$B|%p&2!3&H|IF9ml6WuFv0zI?1DP%D7J zZ^8GGkA&BaXOM@1JCtjVi;pvjW1B;YJ%{a#O`Wxp6_v&8QQIRTW*4TfOtg%F4ATr; z^l@|>bP}{#G$%Bw)aBGj4~;-gAUrCE2YnByDg7xXDLBYu$X3ZjNYhCUNtB37h!Kbk z2pb7;2<-4b;XlCh#vR3F!HL3N!4|roigkdcfcX{^=AI5lJq9MaHCi_s1*#{?FbXqr zIMM==0OCu8T?AS90=P>!E$}ix@h=Dz0`Ys#&TheDX2QdFm!Hxt=k(<2OJbPAeDCjt zxL%zsju%D>i@Ao?ZT(daGBor+f4GX4yUrDIYG@Y9-cO9W2PX>oR&+*}Gwl!ye)r-q zhkXZABK;@joqhu9O8vB549KQ^#TlwBe}&DztV4d}ULQVC<8waQ>_EK$L>Y0Z6yWw- z+hiUt{9_RPJ#HdF(up6`?+P?ZiC0bWx`x4&h__QhQ%k%RcQsqR$+!h<$BmOGq7Gj7 zQ$4)vJNBO-{NY#L%UlJb)ag)n!WC)ETs|=CvBavOJ;*NkZYxLdQ!%YX%DAW8IfyC* z;o2|N|A*wh)45wgCEioMYSw_mu+sMjN<8Iu+Nt*6k{AdutjUeHOaL#;R;1v}O?H;| zbJJk)ZSGBt(a_>^R+Apa#Z%4(>XRh#fg#~<g@mS(#`N7EsM5^d|AZgeHa&_xPY#U6 z#K4tNc25>1&V1$#SV_}1`0qV0CtC)-CEn9|7*j#{GgaTb<nv35y)EZ0N^NrRdc#4k zx0#8V2`BemHW{;D)t4Fgk;a#@Er%b~T@J+Z?YBKw;xd^a;<T%7><1h&H?L#NJ-(^r zpHJ*jO%T4?vqm^Xwv<Xq`>Y+;Sw$+~-~S!h+bN;hWyHZ+Wu4G~T<bcWJ9Xew1M^kh z*Tpu3u;xVNhh<j;Zvs&gl{jsG@^oo^qiBc+5A*3hxm=||8l2gz{zZki`6%lbFeS{b zlu&ImoSXACP-T0TUyX5<7)5EC){484qwKSVeOw&l`StN)5{UAVu5YuLtikdt6`Dnk z;6hjO?tHjYCh?16;-)vMV_(;UK*0gG*Akk2#xX6m*MbGMo&tHvs$G&}PsU5;S`{+8 zB6&i48btJ|7l3C77b$_e2Uqz%;vX{2cN{^A2qGBwz9I)4bv+Bv9Eb`BQ~KXtOK3J4 zWfDg}^95y<&dg}+fB2z?*L2_K!03_lBXs#?B=y7x@I?4=<?(k_jJ%}2knPgN2uVyc zPS<9&zEnfk9oq5VHFWS`O26A{3C&I;0B)Cwu<C{5=<$QMn!XXG$>`Es!O>*0d+tI< z8VO1)fTyh*!ZG)Xdn3#^Fi2VCPpf!B5<+?XDc&S|bGEpncN>8zeQ%|NDj)wI!@Nlw zjxQFeHK`O4+&%s>=P}U(Z%{)!<=y=|;T{eUrK32;jEq<gNdzaer-}T%1IfwY%PyiZ zF9n;${MVFOm|#ku|D?QAhXE&i@b)8G;hll;_Z%Vx@iJyc59s&l2lV;W$px)np8yV3 z`TY7Q<RN{YJ$HoY-d{wL>97KxaV;Z7l=~Yd=ii&>^}&?hw^Kqj@IBy=D=C^g(p02H z>6w72gOAtbCi;27zLJp5v=31q9bj|d>;1Gv=C6!mjYn0Nl(9H}$R5?6E?_5PjU{;u z`UZUuq6+qch@SsJ-svU%R6_j=SIKsT<*%z~b6t$@-YM$Z55{1xi0r#P-XA0cJc4W9 zh^Edf72LKim~;#wL}P2;Ps5IRDG=QavoyWC%=-Wg>3J(8G?hdPLp2>{{o?)Z1Rd)w znex+b#?#Bmy4fm80imM0MD&1<tDkk1K6ikB<Fn{pj9k+y>_A@T^(a*ZKJi|2<qE@z zA$XS_4CxuRFyk^~XXm&}J{G9?6=sQhPh#i?vxj@Ti>t334J|+ac%MT$Kq#FtP!5Rl z-Z@sh0N-2ntCqi6_1+>w-zX*iOsbJl+778Ivrr&|z?6`pVF*fS^3m-X*<|joN9BLF zD7BL7Vn?>=ZLiR9Gan3nzB(VrF9tL-A-_p~O{VzENpiM1Z?9WRBr^c5qIaW&jz7P7 z`C*I(KbR8IG<=;Bs(e)9y?oyCk?d+?NjkZx(v8~ZZ@P$dXJk~I72lun%*6!22T-KZ z8UrY=JGYst-@Qtl?-K3Y{dHL)qHq!SxM&QH{vDVSQZ)=wOK9@3&_ZSmk#sS6-HP(1 zisr~C5tAc?N)6VcwjysH;UQK{AWDZ^PK86&^Oz}Wjd5+li6;erJu~ydk6Cv%_%_;0 zl<>inkgj2fT0)bLe~*`+NJwdB2T6vRNC-_|q_u<+$2mzh*t{iTsJ4j&{4dVdTQ8cB zBmuMV8xO1U3bFjnb``C`dxq93_ceZoN#zYNC8TT^qL$F)V*@+wYJPKlf;VzQ+woNX z*krzB^0oob_|YF<BlcZEKobR0@uQ5MnsB~94But+bWu|`GHt3ZqT!p?6?3#;yyj!T zl+L$OLY0p~S9B|ysKLIKy4s+k)|Y`X8W*^wgwM|v&wX1aLISjaC^a~_-wn6dN>9^g zyDzKlCvP}^R-I^l+WcMNk+(>|q7s<W=|3s&)NJs8&;K4D=YuZ)(V{l_KrG8gU$X`C zl|C2QS@aKlIPqr#qWtx16D~!jYBWsxh=v1{&#g<hiXXqjlUx3qDA%El{R~VAX&i>2 zgr=6WUTulG<iZ9!Y4#X#Yk97}cucVRB4t(!JS3Z$J>LNKmZ&hPs4NB=V=yQqlO@T) zONra-`V6F7ecnsi1sNvh@_?hq;rdC}{~+1!G`&J6G)hX|X+3#eH-s<Y+h42~4^RB- z_>p7&x)m*1iuC|Kgz6S*vw0!98%rA=Yh};P6h=Xh|Cr*OV}d=V{nZ*Sg$@|f{#Hn6 zD)~`0XH8jqwVz{-P@VGrcdJ@;G&e^U91b<dIzl*<bHM%MTGCG)(b=bJ=O0wmvqyvL z-d4F~$*${qvuCU4%edvjg4Y{TIc#CdZ)(nSCz+-oB3w^}upZ2=_bazBmX+@66OF}= z^>3c>)jf}U;os*2DpKeioW=1^WynpB6Ua)J(6a*PCJ?zoR$xxums}JNL%@`f&S3~j zX!23``_SYx3|dCzsZ)#^qM~YPhQ1JN&5NO6sg~J60?~3H%B>LJ!LEt9ErMY8jH$Io z3_Slj_e8n(CDCGM%#Lri$iS44(&6irQ01eY-+^KR!;2-=^5@iy7U&OuT5K;i;UYL# zvvt^B#<T%GS{JYG4v(Iub(^Z*q<q&9Xh%}VKKbg$K(f>C+P0#y52avANb4{}Euk9t z9ycL7&Q9z7IGngPI?t*C@wIvnzaod~m4$vfj1!s&5an~_u9Ne$g?@MB4eeurGi&B# z6}$xlyHWMcZ*LE3KGc9IA+^I0wS*=gchfFPF#>H?E_%=Gi*-r&H(r;2{>^wSHs+)x znxdiwI4U;EGB6Nt5!G*$<2zSE(mK|+UznU?aFzEb*W0^)&m~HLDIvYX5VeFRA6I;= zi|XEh*d%Z^$l>roF}*FJ6YqBJj}>KK#2D-(Ujk8<>7QrlGK|@yNAi(~MSHce4XTt2 zj70{nD9yP#atFTzQ(D|g2~|ES7gP%>Ok45s`vhdlL`+ep?J3ug#4^^~<XLR+U{W0b zQ4Wz5%A=!^vbei0(+O7!$t?<e_oaEE^dlV^v)$W|ogPeS{-2b0a%!gYc8}l<nowFf z&dn06iiY5dk6F`%lQ+vKJDfC{)&n*b$z?DS*y9bB+S=Iu_$H7<Ew!af&aTUPwKJm_ zAZ<8U0j7kM4?|EwQ%jhaE&|llekGMl%>75y+(&=IsKfFHd1m){R=7&<r~d@Z$M_c` zDw3A*OAbU!SV#`s@lDev!Nq7MUXNu?2c(Ft!O>$15k3Edywg)cx=sV(66UU%6)}3J z7qYe5tm!({NI_DZ6a*MNNb!S!f0pJ-@6r6@<iKk}Ex(<+gEfpTovB4*A7lLNZP7<h z@-;A|$*qvkRB|WMYf=NI58l|PbKWmh9=Yh8@u8h53z>F^xq$m>BcPa~e}Mf!@uZQD zThVe}mT$iG^|4RjeFV5|ud5z6o80U*@Oneqhb>Im*iCuw<kc*u*aoq!B-y^=ok;Z5 z^iiAaW}G4li7)(ic|f7)i-HM6iLz7W`?@iFuHLka3qD(t`kTC8_v1oRVxL@*(^bs} z7+^|B{V)V2H2KIogqMG;l8K|Nsqjiu<01o*&z>)Dqat0!SDMOlI~y>0!gGxr3@kP4 z&86(b7Y=PW7_parh<H4gQ?JUQ=Thu<22(=%hp$sYm5)<2;j$mPIrqNPNtnxEtoT=X zrsFU~aL7bHUSiBfJ5m9nd^4M~BWxkgDpd`ScDd8YRjtgG;3K8So8nE?cx1wB0;Yr% z5JS`wntZHFF=)Yz@)u2~xevPlmlNXEFLikqRw|_Sk&)fNfC_N@w{;`iQisU+Jkvol zP9mC%n-wx1?9&&AM@5HhZrbDB|MudL24aX>LX(fBTo^}A82pj7TchH_b<YplOZdd{ zL-(h|8SrStm-s1xC~G)aA1@aweA|@G)sbE(FB}XGpZ4Ojh*UpA!}PJy&ID6JDu^L! z2~9qh;pDw<T0F<8cd}G}zh?YM)jaPwu&W~JgWziC&mM9-Aj->^T~=LEp>Hll$I}jb z0y&OB9&qo4aLYM9chfCzFKmG+b#JAFuD>y){pXd<#L;{F^!SU2KhoI2fk$I45p=L_ z@BS{!apwY2{%((@D4FMj`w@%D^*|`)Z+?i3l`w$-NG?t??gVAJ7EG!0pOkkx6eJ23 ze<8|H<9FV8#d>ZcTu7a;sC2cIEFZERrc3g{8?e6!&P$=UR-W)7Tphi+9cXew&(H`N z|L~GB3O17`SO%*HObO{BhM<IMpsjy-{`pvF7fxIGx66u6Bqk%TsJgHLCIwwh*Ou0| zfF}lJM~J_}^(qgVvMkD0FeB@E5h>fmP9J?sSiu}|L#wI)M~@a{^!$Uo(*p`=dX)`T z6Z{{vvfog?zSf!j5%Ki|UNuU|Izs2%)un9_2(rMDl;8BB?Bu5+#ue8h5c+S{Pes$> zg5l`(yTJrUR)4{e&u)c;rjj(%v<5cornO?0l}|*-tf$!}-i9et^ZTW*?SJ1%YQzU@ zFrt5{T1Y7l`%9BkGn>g?C>T$al$u`YM1wS?@nV!;7rfq(B4P_;4r2?RJ54CcW)R;B zM}47hoJ;9uP802^aVbcY(cP4Cv5#mQO_2#W@ZFr>z|Yo$k~~K_WKv3Al+JC)cExOk zZ-f>nGaxuJs|-vDX(EQ8geD*LB0@sDNR?p33<=6UDQ8f^Mh(}TQGUr4Mzyu))%XfH zdqbJfrH)lUrpX0`OPlG(>LZuZHucj964DHHZUcmrDOxZkq>A`DB~<yCk8xD=T})EL zKf$?Na_K8`1blUt29FX<aeOOvMO+M^V5;&ys=d??Pxznv+U2D$jGy&IsQexJvm1yy z79Jzxk#-5Dgme)@)DoI}{E{crX>i}YpiJpGf&Rm~L<d;4wfju)_p8k2t<Lce27xI1 zKEph5Pd`r~Y<L~ix)O1@(%w+;!N&Jt-tJ&_`TGziFeRjn7^0R?-9Ogu+4aH^?=#dY zoxOp<>v$uP7Katd<JR<5+$HW4F$JJJ<<N3$KRsE!-X0mAvaT<b`(DTEWkZ<DDlg3L zLt64eFeRjo7^0TY<RhA;ng7;NQ0I%qBp%MDi<G_h3spyRLa3vcwWb-zVt{jNT*F}Z z_>iAA5;lb9wrWO%{wlZBB#&9EGA$6(SrRi+2U9-1l@h9ad`|rSG~s)^8BLT@(+~s4 z-}+L6M`WJjQ&DbT;~d8|hynMHMY7FDWw=9*?X*O;&D<X6ay5@=`Z+R6EVrw^4ExG} zDV6_|@=n?hix-h!k)@_!Hws7%F#Bt3SpMwVRP-Wd7dYTs)iJao0a3nMxu}XX-T!Hd zNfrL(!wO9<mY4U^qo?c(bW_xAs9kJeN=PL!1SK@JoLQ<pKV8CC^<m59zQih7c_3b_ zRLJ%!YQ>+0s*8Yn8Hh4+o^oMHZR%hRUUCa$?0#YA_TWWv!^Wp_p{0oLe+&%4(W3|v zJ^zEeQ`;!{!+BxBbZ>hG&R9PRRX0XtY2zs5Pfvg9v{cyjH{sy|L6+K6<*qQ!kfBZ^ zu#A@0reN6>Q>w~c@FX%uoCb*sSAroGZiR%ZYwS;qmNBQ02X~fU&jG7+ae=AE<Kz)i zG3p=TFs|*-v;plTB=Av=uRUw}2co;a@{rN`xOoW%*hB<N`N3u$KE!ANulEfnF+UqO zukoE80caN<uRR<aHcI_5mD&J9aq&JyH2raBxG0?9nSPsbzZwvwr|kn89$aSYcZnG* zUZ<69G>O8CU&Q-3NvH&n1w!em!IU?o#MddIdIiv8KVejK$n8>M90@xPheuPfUO1E0 zXQ21-D(<lE{3Q@2-oqL^Y50H~ijrZBoT&&-g4wMk3dR8`)ipFi?dQ9dV9Fa-;%k&p z<>OYYSj9cunuI6GMW3nBuJ~UoJDVZ$ENZ-)!~oeh81Ms8+Ab5rq9TUWe^|;q9*Wzy zzz;4n>*I=BGWWW24ifqL2~2rIOMJbS(B$LRM+LRi80pgR=A%CYf-L@LXsOd=VYIIp zlwQkBtx5yVV<mVfrV||yKo|1pHFuFG^B65bS34Wr885Q2@~)`sS8Ool4KMNaT0)bL zot6>4d8zRJk|f{uxm+TA2c0^5n3&?C_oAiXVv4o$0KYZhaO+@uqkjkYK0fc+fr0&~ z6}TRFyieTSG3a^8p8N0Q@`jlBdM%;J$3<1UuRk{(>HFA4>O8ckX#UO&W2lOD%Uw~` z%4_#C0G_@Cwj006i#qorw4UeCd3!AQRQL1b29B&}2d4c`-)&AqFs0<Jlu+d(*XS>E zg}iqtoGNEQuos?bgEKvFf5vS+ydURVCe$$40#O#1MAgv|z8jG&SWX$#^BkFeqpsh# zWHJXz`l-|=g~bb|l=x4|JN>BFQ8*NgVaLDhmrC}z+J7z^aAFX>{cPn^5zqK#+7g2m z5G8T^R*uO8{uetpKlVY<WTmAq-@maOyXKYT`Ng@bjQ;!IbHh%2of4W_UXDa=Bs9tU zwWOXheypG!!;)@EeB?ZlUY*M=@clVO5MY1K<hSFwin<BD+IqQ>u_<}Bgz)O@F%D<@ zNJcBlnTrH)^oT)p+W!yoPOZS}f&ticX?<b#l2b*WnDLb{(#&(8oD95_|AKfKHyQ`H z;QJ}Pl7HM3zRGM58(A0Q2n|@x9Z5aaa9rdXZ6jy9zYK;Hy%iFgO1h4wZONg@*x}Wy zh&<@AicU*NI(0h)F*jTO2+yatCjf#>$nn)t6em7=F6+HK4SK3PxORE<Fi#>5ybY;E z?;&youlEf-F`p@?i5dG{@{!ebF5PZ_i1dSNrC5)^@Ab+}52Mt|=z2aBYe5NJjw-<Z zRWuImY7;nJ*W<9F0-?wk>n!?au?|?M<<eTaIX;TYV9FbQ;_H;q<YVaDUJh5>7$XUB z{qBrI=T-iXteL}&r^I-{zVEHlb!UMnt0(JK&_<q&mxOOWHsuD<$GONpxOAHjVy<yy zAjRa(15@4*6knr+Djy5@j7VirOj!_}n<GbZoynicZjPB(7as0x6Y-_i{03a{qqAMv zh%oGueg*-d(0W+}#uU=bLgRs`)INsNi)Q~4Fy#$H@%36llaKn=4Wv1e^IwkyA6$|3 z<VL5emJxX4;_$yH4s(BgMQ;aqv)A{sYjGld(tm9{#=~6Fi*S)7<9I!Z6)nVp70YKU z3ru-KQGC6Y(BxwY<3@}1p6lX>xuRbzdFjI(|NdjYQ<(Mu8bVvyDN?{mnC__04G9s{ z+lk;`>|?UwF#RGv(;@n$7x_vV(WAjV={cD4hNJj;EuqOrHUGbfGB)V<L1}byGAwxx zLdDBdo)nn=Q$dm|rt^n@Uzcs9sF6)Nv;%2^&(YvZEW4T5@$uvJBMX{H-`nMKvi<8R z5xA8Ss(h?0?qRGlI%X_FQ96~wIaA)29<$Pf^%%JKyAnO+uLNL^K`W8wBlFMbKA+Xl zCDm5SSB~!6Gw${M)?GhN$4sVwtOBOw|1V0@J2jQKI5izm=W8#KExn*)PzZZ2A9E5p zA(}qViqxoZf^WD5L}`|hSFxaLr88&p!$?9<+}ONy_KNZqqS<mTyh|(0+`sPc8=~Ut zlu!-5P|hjt{dJ*f!ua^J5W<9&xkCIOkI33rn&Po<N5p4NfhZG06X_8RW-7aaVNl<$ z9DMu2jF`VmuS>F#0h>g*=xGd&9$tv(`5)w+{NB?ViM+e2pB*?)M4jYtQ$pWeB?U*4 zGJi?zDan@^INb(3P#0r-_3mX*8pTc=r_It3{KnZcX778bjtKrE=P3$4>0n5nTOpyT zWJaYYiu@F<rouzM-AVQ4$M*=9l)iN%KT`bs(Nj{T)Eo%%%%6O#r*%}0T+Z4Y>DP01 z+&G=Hpj<ksiE}DxNrTt|@Os}c74z|#o3r1^!D;nCQXweQtY{u>2$g>?STDA%sx@s` z2ley2#tp`z62MQ53Iw(Zh5H9t-(HNT#Fu~Ac=1l3j4{n)Nt1t_dl>G>6ij(TReYTi zntTj)YDy46`(oY4*yJmD`ZtCRWzIypY8B~Nk#{*==R2UvD~{z=^LwhbyaB$G2%|F0 zs?kf1wvJ2hck6-oXZ?>KPk<?JxQee)Lf5}4QyuR3%k?J;X!jZRA(3)viF3ki-B^hZ zT&PwK{{`UA?As7hy7)?0_<P0OV`7>#p%-O&EZ!+CJU)w)Pl)hOXTX#<WX0EO2~9p` zP}w>X{PJ(YBrkZ=yU<-+G4MgbJi0V?b?wF1gd=yr!xz-mj=?|*V%?1SRGElAE&LpO zDVb`#BD&e_p=c3-q9HKl4O{W`T0(XISo47#<=5&J%^Yl1-YX1#cS1*e^^wyeT9%y1 zS{c$;fGd7#f@@N)%Rr)$zqy_HLykvmSnpl_V?RfVB$n&|=6~H9H+03<YY9z0o~E;t zg1&6dX%QNNV1hMbQ8nEP)f;_tV!7*8$))rofGD5nm3g2sa@fwfuCf#Ge?%`s_r08b zWF&7)oY}#Y)PM}8WWALVs(dVHCapzcDaaMYE+e5W>Ow)LYUm%4XkdECQdxfaa}MzK z-sv62csK6S_>2fbcbev_?bg9`#Iz)T>589$no$Lt|8DPD{*&@fDoz?*+V4E-L!;E+ zVWMWy3|u}+Nw>lKul^{~UP+sLZCV0E$@(cQCet3H%A|f(`Q<SeVyF085xJ5#zn;`` zz1d&{E|~I$v-mnCG_~}URZ=I)ujA6uV_F~lA}lx`cvxBU*oLIrRQ;Dh<T>E9f#nQz zebu#vgogsBszSx9#On2fBcE+oao66h!)DD2z6M7RGi3DqgS^vInUIpApx~BIG-X*G zUr7*+DEGPJ5!W*>aIgn4GAvM>kO8l9r^BTRo_UZzKCTiE`|+BjR3KE`gL}mCd8A}- zO0E+H7?SB$NN6hA>?*lu{&J4rC_@bO`1d`MBL)BH*RSoyeH*wtJ#)7KHQI&shg811 zt5HdfqNfX`<dhQ~Cb7Qafo}wDWC(F)@;SikeM4Hz$Hm9NbEoswwj7(VqJ+!XGA52d zkppJ?hIPi?CyjbWAGbHdBvHZu&(qak&c%odJmfYktZO&Yz?)y`#HE<IOe;s~ncaKT zZt?F0*9~j&bxLURkw!&f{g=Do45QsteA6^2k!2;}{l_9~0`^KFf0|)mz!cxm?T=2X za{I#a2F(@rM;jZFKeNeH68r%iDk1Tg!?rpw<qd7|HA<-R@oJ7a%>(CTYJ9y|p#6<8 z|5!yANAMDT99-vmWaQVEfH!+x)=6B>-t{N@tF2-_UsjOzYI#+e&bHF<WK2Qnr%&U+ zlsCM^*J}w)J~EWse{B_(DL}!pC6u_xr>=oZuK6tG&xZ5!l}VRm@H-$%;~6(6bg8gx z8CoxHEry12^YH%9y>-myk2RNy^AVKvz?3({#n)>IO+G5oCt#6pqrLS>h{;Oa45=7N zNNHu1=pJYd!%a}qWC8rvaI%Jx5yeT?ZB@%dPlBoRrWy|5>27<qO{NyfEWqMq38uVZ zF1}t%X!4Pc>qyFqz05MTfMSce*wsPqk$fonpR@^lt#?aj)02UKZ)KQ5w4+Nq;QA<e zI*^Dy#j_Uok(d%}goP$B2~;aRX#!JH-%1HpJ}Q)ahI=}(_T@sJ!{Sq=yK$SQpC3w6 zlc(ihky8v&5gH&$bWY6Lpx@#g*-yIo(lNtlERZ^|nm(q9pZ)3SyDSL*0;YWUpOkkx zU)4w~;qX@fTS``{RaxWH(x^U8G57XVbQxu$oT}kUff|UCne}70JsA}d@8_>2Ondfv z1tWxSBAG1Q?Kl_2q;_HC!IU@j#n&mJsimZ5-m?q7DQqRKvcC48$|Jv4gX1X{tiyLm z$~57GvjO{8m6>2D>`P3w@)SDp?(H=#?Q(5*6<=s(az5<z%iHtR2S*PTWc2)lywmxr zF?AJz;z<ZBs{+Tl3D-ln5cxy?DW*>t4yx8iM=mme0-!k~#j~eoahYB%8<<>W;l`}o zA6|GXvS~W4F2gREHM|2uKDZSUno7oayD6-z`p&C+qKJKC&c?A@TvP}yc~s5Vkz~|{ z{Sk1Gr*Eky=-r10MiC7z3!T$XbR|_t9k-Hyuqknu2Vo&l|Lf1W;V<T6XJ_NQlbJK- z(6oE=BTFCp##bY;X=;&`H*kD0IT1tXVi|waV?+TdKP9#$vZ8R7Tm-iCZ_>t7><c(i zi0UZX9fmA?eT&v$4W_&yFuqO+O+L14dSPIrw-(;}ZA%qjz=ZKx%L#E_CNH_RwI!77 z2S4EZ4jzR>HeGT4!&7AOv9WOVpHo}7F21c?s<u^jE-K9x|8}`I493?ep~^?A9J?z8 z$2V7R8a-uH8|-aSMhUR0tl_A3dSR<@gwz0)Pv*H8I1i|u$X0S`x`RjQ9DVL%lkmal zq#}_R=6ivPioujO6vo$U2~9r2jZV%wfA}7;wTQ|>)Zax`V4N<%X{AoQ`bjmh$RPmG zjaU6A=jF5PlW{u|`EU0te!Ue}StUF9XfFHCp{{NEVb8x;nl~KA*J}w)KI(+eN5H<_ zT}D43xD>G7T->#{Jn~3-Ck29w8x$_G0X%&fFx@J9R^P13hTSoB*!;>Jrnl6DTi6~B zV>D#8jhSy0OnF0Me7%;?<YSIP5oKUf_H_D4G~&s=gh9RKMHbfhA)cy5r1k6Fz;+<Y zclpK5?oCLBYrl&ko$9`#(d`$1)BD7RaZ)m$COaqh?_QkvR!XSyG3~RxF<WiyY;-y- zMk}#ofA)QmP)9_G-;FQD4=BSJ0K40SIBHAn>s<8qi!9K;)2k`a{V64SzMF&!H@rk` zjF9^kOiA>gly?$zzBhkEiKi=_B|0Wig83>#Imc211Y&u8Kr_q7$e>aJ_;rbhM}j3T zSUB<k6=s)n5Df-f<HDiW=Szrvz3$SHbHxgn@`lOyIwdr<<Qp>H*O&j!%x@`P5Pqdf zRxtYX=NJg2H63EM8%~S_*jIg^EflhizjMshcYh{t<na^QQY5>G#h;|^XW8jfv9;#l z=plfNo_~;cQgm8GVSa0UEwaGTSn!275IcF{D;bjK+<Loe{olafQmVm#7tU>PhifKC z>A&kj<g8j{xVXY}p3)}MiXe<i4iqiXt%D))Z-s=WlAlsd@DCK~r{@Yl$KOPXgYsCd zB&z}>*hTasD<!n$F@PXL2=v2K`pu#<7G!$dB8e!!?dovJxalmil&x$RIJj1W*ZYRb znAeog%=AwAsHD5NXjWF%`>Qm)m>gf~@4j!$Dv4r=u0pL{l;ht^z_(GMutYn`CA|6B zMlA$CY2LN+|0$8qPPag=h&C4)MW4tAQ{HeHU#Em7A7>?ECZ0b!ozH&((nQDNzo(U` z`A%+kdA+oasB&-r0I&lO5dj7BU8=?czStBUYs%!0m~v-1vYEG@Z%AFgt-M<N=UTWS zGrmR%RX%cYtCXg&4K6Vs%<vov71TVU9=-bX9(CBwSy9;b!5=_R$%eq>F-`dc1mZWX z2Vcp5c+Y%5>f}#3e{R*OGq8_YBn+m!VKcs7OK9?uPGG_6Z?4#r1o)t2<zMuavacjH z*9$w%vZsZkD)|Zx0AGv${L!&s`1Zaio^$16{j&(Fbb~bp<inx|1(BEqKaCZ^ls9z7 z*J}w)KIU24jPx|ai1FU9NIs6tgmI4TxD?vd)nOdQlMRTk-~)8N%n-9A7Rd~72!>B2 zgily?;tR*m8KS|?H$^-$KIr`SO7n)#_<Aj&$;a`uK^AWeyzDju9Pxo^NA#3gmVgqO z9?5Q9-`V2JM!@-_&af|zAFo9!vCql9pc&+{7K_nvjZm4c4{)t63+h>}0#jn%N(ogy z(p&BcDm&E)f4_&ToNM}N_qmET=x4jOC3X0goGJk#pb=t_lkJuAj)%Y3j46eQ(f*8= z9*^}KnBnTLvCEdDYhLfcl=uFV@=o~}M(i{X66W&%bUy*pe^H=2k0<5(lRa2lfzX47 zZ8L%gK$Mh-3U~;-JHcKVyf`T&NFG(Jw3#gA!AQGaq{?Ao?+3w@H>AebDWR$5VYOK6 z^LXY*0fHgtRGX(JEE|^;hKEK~^z;_mzq~R4CHnZn7CON%_u|Dmq+b%TS*#EU2EE<B zSBiQTJ@M*l&x0HsJ?Ie8^FPQt<ztBj)kd@}_9sPH%UR2|`-K`#Up#6%7hdh3smiZc zSX%=@QfIa+@xRV@6cm0Kp?xsnlu7eFaXkDXg9n0;&dVWPQZOXit&q@EQq_i2q$Cob zJ$n>%hL5fIV@&nU5GED>tilJk^+*QSc)%}UkPM=QkGG*KF}I&MRr>1jC1S;_W6N9g zwlkRrejmia>wUv&%*)TkZE~l4l$iug?-SIVZY-NWEwlgp&0l7fga0p^`ASXb<P5bb z;O|iCXC6Y|a^rs2XdMCnKuq|ZL+oZI^M+uBk!T0@Cc-o@<qfUzbxLUR(F;}V^#b8$ zTp6JiA@h4(leFHN=^0~2)6k$6!Ku7ybRbI6VNyl(fezd~44;+D06QW6spryKW&^HD z(#Cra#VG#uLEP{fU!#OBAI*AG6(`e_cd@Edq5_irrV|43<m2cmwbT0a=Cd%%fGBgU z7L8DQ{C`E}XX^!R!9SrYH^X%L#uA|+0dsFavOxz-c|&Y`y_V4A<E5^-!>_%ejLwW~ zUBk$^usMS|maLVGW9+!YFv1GjejrK(0nhJ^Imtx}VZ2`u9r)Ij5a00m{X2p9HXJmR zX!);;=!V(&dM%;JN8;3iZ1k9nw7mDAk-j-ex)M?37eO)cXYFbH_dhDu13qz133nhq z5CWY&C6)Vl)kh%9WfS4~Mc=~x<1dfK^$sFYFy#%k@%36llaD;CxVv7Fm1-?Rq&Sb* z`R_-=#e{ok%E)Z?y7#S}Hv)<-F)L%SrsWu_g*!c!zKk;XV3)6;%ZYK@k5x`75|VKI zdpQArD<xF<m@CGZ);gE{@Deklal5R{>--AQyULw^s=>nYm_Pamu;)@~1p}?J-06)* z7LNi>ahD^yi)-7FcBA8~?1*yR8mxbvp>Y36d8d4|P&Dr5P?%?AAIqd#SCz%D<o9@6 zy5aKT87V$W!O1Vc(X-OVn=7%PFST5QxLEA;Sav@`D2-``-)C_dlUx4y-Lwa$guR^- znp(Oky1MnXQ%}bdlFj)>=6ztTdqedDZfuW;y(E#vZ=Do~(lq6-=D1=P3Ax(OGS%@| z9qI?;V@cC=?c{U>N#-~d9pLDJfsCGikax<*)*4>7*MwflBzSsz>ak2mJrDK!8TOm( zg(JG|RiRIa0=B+nz3&zC4V~oBE<2O;!aN`_S&^I}Rz!E9QC0dNt`{Ff6%=qQBs7&o zUb<JkwzWP>R+<Rvo4wa!7y6g0K=q*k3pLE^^D1T=Ajs+Ns>vEHBHi9ULVh2olJUOu zx<q;L>{O9Sz8|vQEX)i71^M4tZ*F$8yA64GKHP^)PFISP)j|Iq*5Z=2FMghklF=QB z)jm-+HOxu^h?2_MuvF+rfVh5hymp=!JGDOo355sgTVagN(shgnoBt^NZl{DMA9D!2 zJ`VncV^a!#Mz~T*Bw#z$Pd~{Ea;kwfIHvltQwK!JNvJ`IOGx9bZR4xwl+c)h%BA36 zu}@&w1Fr{z&(DJlru4m)5~_R@$ac)q$_Zy8k!+^Sr{kaeY5phu+vK)po(QvcAKoBf zw?Dz9gLgst*E9F~5fkQy0r7h}ub4icOE;uwVl5By)7pb6eQvKMH2L_x$QCB)!O)&8 zJ=RB_2zmyT$i#|btKOniULD<sgU$B<U*1nY_*rfNSNsflY^+KqHIF~knL5b%DfX5m zjc8W~HV2r}`}SHwwSP1a#DCo_>QT(f@Iy;ij89Wp>FDe|Drd5~o&6EtvIU^G0<QL_ zW$tT|wYP6G^Gz@FhQBh>u~5%vRqPXM><_>60aJS2UQ1~5(cI8BPHoON&|~7^rx!c6 z@aO~gy*3L*_tgj=oRhEj01jY_u19gF=K3bIiI<~2$Jyhl?n)bt_YAS)H7V9_*|m*H zFs0|Mlu+d(>M@y~nR#JFp_89CXqiW~Vb;3>cQAudB`KEfZ`Uc{84*L&BibqpayEP^ zhMt7E#i(IUq~XNW?;&64zw3SSUUUOfLYglBkMd6Uj{+A}0(><*k@cBzr1nineLR*! zS@}6jp`O}R9UxURz)py{?hLMUmdMu5pcxOPo^ZiqlbpWJ{SZ5jueJ8(Iqu*T4}vsZ zLQq0e%lq*hl!J?BBi85)61jGpn-^HVVU=7l_dn)ZS%&hvAplV_?ta_+lm1RWczL`* z<n1WY(Zgn@48nozEU$UqG>$$4aP&NfjGljxce;O^w;vixr$eG1k5Rb%7;DirCPgC- zN1`clPhQXQQurKDUc4iJppVfH|J|tw1D0&uOi-lCi;BTxlr?{jrSI^oj~*D(?N&%= zDtXeTZ=~T_Bj+$zR%cVxYW*{VM2s)~rTzR9g?7XP9l&o(r?TY-qpTc_TUH{47FLb! z`W1rDU>f;JLY5WyZS1OD!0Qd^xn$#I=QQCoyGuUS>rOmwmmGYJF4n#*#+&-Z3yp@J zI4v>hOPRaC``{EnRp0urw;Nw4ETZQG2*ir=nBs5@ck;>%=4iOzren)jV8wwcAw8E6 zl+fhk11l}vtl!;@CrFsxIl6r01;`IW2RP0%d<*@*f)+CX?;mr`8uzOgIk;AE*93p< zok#TXX-O-)xpos{?<tu+;`0VmLV7N*Q$m%G>J#%M6Q2f_%(XFIz2eVxb^9IdHupFI z&zoAcgpDc?@U^&iaui}W4KgBYA6|A#8gV!ZXqondcZZLisb=;$dX)m064G-CQA=p@ z(N60bsy^I7g-tXOsfie&94w=kEG$j%Go=y1hHdvUB_PVraqfN<NMmowBayx#(f57V zp*Ic4$gnt9^mKgAgYNzBiwM$l2~kUE^3hoa23ag+XRJP(MgF;bv{TFqmrAVs!c>@W zUy#%WGT^ty{vLy-CJ+2aE6T2QVVa-cbZ2m>)jcvD*2E9l|9t*73Z`_py_V4A<8K1w zgt#;VHdL9}HVtkzwUnNm<F1d&%31X_3UVW@fcKBh6z|?Sum!pLzrMHn^o;M#+*^uW z$4f>(X0@u)*29ieFs1#ilu+g4@^M6#)?-jjF&np#p9c-0aUaN;q=jPo%cc-YAQ2nj zWQ-}3wf6S#F^>2wt%>M)f~OLjSDuzVX-68?!PWlRZ<@fAkfzK3qrB6m=X=Aqk4q!> zMl{K&Mn!yFKMxp3)&9o5SS)j{mi6M++XMV=i=1b4J=*(&a{4n%Mi@UvdU0fU28SdP zRcKhPw9`!=ObKbagrJ0`mP)!6LpdKt;S`P<1`n#$11^qv+fzujv+!`JqNXOE0uJ%9 z4KC!YDo00^#TGmb6b@e2bkn*(D(En7c7h-MGirPv96dIW(en@TPO82`2}+);un&iR z{c#LuvSj_XbYrFx$j$NaaG4awc(#CwEY9&$mieQmWCg-SrVS6Vw^Ji;Sfk)i|3nik z&~&2tP=O(>Z-s=Wl26Qbp3&P;x|xrhBvnUbgas|miUmjZ(eenh;=9xpwF2ItlsqN> z%icU{dboBv_!RS!N=5At>^>*&RIN6LgHYrZc)cM#mux(y?Cj=udjI&kqk2yUk&yzi z2o+;cBAFhOun^ZxWj!XKvwx`4JR0ySH|pV)<ZCl|1&og?B`soV-(tJJOVZ5@46REd z%H`DW?0_jDJ(m!a(BvZp`!c7W<HY07`U)O!!4>G^8EBbapfPu$4d34ec7=d{l}3E- z8dYut5fez88Eqok;t4b@KU}<L4Ms7y<0p*o#eyjzJ(t%hp~^>pt?yC0^Y+#W=f62R za!Od<dXfn6l6J!DRH<@^{yP5-L|HF`jOj|N%&ijdmx0?vo<4Ww=6isW8*i;8d|>wI z`5!PPq~{W%meAy5ef?}jQ^cO+38gK$)-YMKtolc<PfkJ4nP0V7a1T#408!$s2A`*l z5pUTIU$A<eU>xALE>;bHYgjeh;YI4`e6kOwg!EiO)DoI}lnk1U$N#KN?~gE>O7-lZ z(OhOXC;le^s}wfxz`fpHz^}`FEPVGXI7ZDSbZn0ro24($T8ADdn6=d4zvRYf>u=-+ zQ<~mhOK9?Onp4bPvtq{4{^1jR5e90nY=u7Mz%AOcPpiuk*uHT!K$PVy5?QEGm*N56 z@dgD>!smaQ_feI2VBV(*PL$9ZY52D@Gr5%#x}oy-nh3S?AJg)-<<Ow)M@JJkRVz#~ zNl+cmjPKPxRnh~ZBwg*dE)z~7<4PUmX4oXQe!h@6MClf+nDLs<<BNif9+(o+booC@ zvpc<iR8HzKUp#OFRR*%jD$!2`R_AuB*`Lme*xD9$Mp2I*1Fm?#Ir5syO7g1$Q&!$* zSYhOAD>~iha{8KD$Pd~baUOvwAx)PMlu*5YG;?`mZQSqgwD4g~dq6>0W!Xo5zeaG$ zMnkp2>gON8ap*B-gf%<L_2}D-enL-q`#YaEPQ^4CNZ?#}u6M`r<e-3~#}FcV{s(!d z_m6Y58b2s&v7*lJUogp^_ie~p>zQYG@~xd%4%F0Cz48ORa0aoOd{J`kZ6wUJaF(da zLTZQ-u7Z6)!)V2g=#E>s0){lW6%v|Ct`XXQsF=v1Z+Q11bC&w5>dG4Bh)?loDe|}4 ztWr7+;BMmm3fGdoxQKkqL83#jf+AVFoX{ts+`UA#5z~4-?GXd;dP90H*?9PPO-(KC zl8+XSZnOET*sey2xIWnLS&+h=1yerIMV`C!JE#&ZC8GjSGCk*y-1iYKC%9s{{P4Ye zL~%JoA*<qd#rWp-?EA4s5iljB=MsVvntXgZ6s~#Y=&-i3av8~%gv;(`-J9?Ze{Nn; zEm5;#=Q|)}{jb`BlOQ#eKsw)vjC_=4?y`9{7h5mGM$0$Ra_)HDU`j~O<#kG^@^NW> zD_u^(u=s;y4>9+LrEWG+GK3Rep&2#Lg1pgsB*4A6jk7p~MakmF;U0qw9jP_s#(N#> z8&>i2L0+<#0&U!8U`j~OB}6Tu$;V25U%6D>mMc)?rucc%V+>bc?+vYDvgWF#oUjyS z9AhBL?dl6Y(bWRc*$3^EG(irBa=tJ=gLWzHX_hZT*~E>gz?6`lONd%RlaK5btjGL+ z%<ac%x;K@1EFU%Q{L1Y^FiLywR*J>;;+rZEWgV??@>ndwyoNV2uM_p!njTr0M;-F$ zTpHY}(gW+*05GN2?X`p^AGr!&|DAfp@7lqjW~|`!ryZoUx8`h0wvj}dGsb0gJ`F_4 zj<k(0b2P1=Sy<d3yin}<@>g%~AzFxWql5K$EyhzcFy*sbDWS^8tC(VGS&(CU42j`a zw{hAJdfjQD-rtSPdqWAuDI5&&fG3x(TgE!4Lb!{=G9P{jlzk?gn#ki-Yi<0d${Z!> zjL`_Dgfw0LAEnt{e$*RndW!u<W4i-97T+S|2NxR(mL(aj?-bvUn!?F$D<%S>R7C9L z%PA;pQDMvTsY7^?Z<~BLw<B!|8dhmrF^wH{1XDtqE+HtPsinCZW+j1%B7(v5$gx5G z!|?YV?fL<K*6;l;x8)l$(n<oNylB|9TO`SrpL>g>DM;ufp}*Wx$MhqFgYM!Bv1pz@ z8aR5?A*1IX<ejc^T?Wnw$$p{^%~Wl>se1;P|8RcT+-!1O6!yvM9)h`iFA(GdwfYl| zxW5Z(sitJfg6`TBAUH)Qeu1Ly)YIA*<S#VAkZQL=LQ_dPCniR6Ho^~}NZCvDq?tDu z7*3p0k4dJtqQj?g4Nw4IaN%ZX1|F7?;|h!ZP+3E2?az=vsHi5W3&|Q3;k`t=pa8Eo zr00^2hl}6B{7&;9)v*qO^(cb7K7<4*>VH*R_!}RGw>OKhB;y+<>8|4gxPM&9SNC^c z7E#e5phh@+lv)2i`e%ZBUewGHbEyk8(c6E?1L?VhpoAtL`@_@)vN4#tzh=}k_~X_3 z8s(@@M2kz!x?akdE)l0A0#OEdq^^qv`#ye|6Idf{Zlz7NArv9VLdfpATR5=VMgFgr zke<uylu+ekhp%9H;IhyEWA81a>d3Z+UEJN>Ex5Zo0Rq9@-Ccvb1`QD0J-EBOLxA8; zg1f`*hVPEsr_b*9obUe0pSNI;F|r}gOfl!Ps#dKv_w(cR=Hj5FP`zU3wR4ub>AMya z1p-r|vV#8<<>+D~%UTq)$a}&WV4A%c$@#}@3B;vkR)}mJfA8j=Kb`*fK9_$xm;cT9 zSWGszwK-k%i)S2?`jJ`LgM6{SOrB=&7_wH$otdNlKfbTbN2lBSrn3=-V}gW(lXVqK zNUvMHIl|Z!h<<N`{YBrZKcf75pUc0U%l~G4ggx?~Y!#y}L<;YqPRHPFIo1wO<B5;4 z&5M&-z=Bl#kFzf$jmkYa%ucWyTaR+=HX}I=QJpJ&7u&iCCRPuF#6vuPL@E0}pUeMd zd{mw{g{#)WG8>q+tcBg-6F{4Ai0RM0CW|gQLQ5pB_>X(`P|GE0bb3zYyYDM)Hf*YW zt~a0HW}Q%pLq@MepG-UdeBvzgKSudqjgP?Ce5ZyIF|k(I4Uz1YNfogyzNj^Pl6^>O zZOA}V+VcM?O2vz9cAK54pD)=*0%ZF-xu-%I8D!yKr@oH|u(p9kCjAlRzjeBNjq?9$ zQ#<cB99ClcT`WcQkg3~p>S|_!l*s;3&0o?QQzPzUCK~^-znt=vIhBdD0WtbfSweK* zdc$g%c;h@}s%7eE+8m)-@gGtCTc^u^jq<;GSKrc3zqsh?{iI2)m_aoGqZ^5MQ?yn2 z)(tjR2I&FIhW`}hgY3+gr)|}>WRErPZtgEZhs>wtN;oYE)~(UJL2TfEE`5>u`_S`e zkpEY$-t*AOu^i-M>g4V4-w!ranpO549oEo)Erd%p%b>uc{Ev^L@+afeWw>)JY(Qt` zK)M)N-e>Rg>a1R_*f%eAqTg(6{Sl<({}|+db0%A@LElFTn*XZVAzBF(-eHvz>q1y7 z_X~GEKiTtvgM9a&g4~4@_!%2US#dmzp|HZsgEx&up}(|VqxMTCw!%OG_0Oxef9rF} z&Zy7M@_+UIR!AJl5Z^v4c0_KNdScn?3ja(`Mkz1;%33yAuoF89_aB!6^E?1K{rWr$ zDd9+rC;08sRaWsx$=@Y={1Rt9HD^lf?vE({t<UAZM)}{2k5pO3V5W>N0tg*deubY& z$0)R?dYzNVkBaXu`=KuQ{^PFPU-etIplYJu3pec7gWd_7Nbq}+h@?pk)jf!26zB{7 zd1v--eJ=kc%KvJ794@bLdJO4vG-V;!gGhw&R9e~XoQ`%e)>}sYSzn0wA3N@{0CyBl zi`4djKA^nwWiCVay9PUU_=PuX;x-*wm|mj?4+I2+111gR$?x*XzS-*5jNQ;lr&|3~ znO@#Xx<q_mn1auUGlylJfq+_z{43D{9tNfoN*w$oG{PT0^uNdd9{+p%@9}no3F3Hz zp&=lk{QU(q6jlVFKRyr;fPfHCQ2gP5Q2amnx&6N%|L409{(K$b|1bQHe-sGF4~gfG zub{xd`9K<f{^cKkfUFFTE(+E+-<^8%chFcy0;|{*1OIva`Qjg+k4U+o^hL3W>gCi% zQ1vDWCs$Zr-<kwBEVd2fN^0N(4OPQx?0>4muTDMp^c32MWw*{<67z``lyTB=1M6MF z2bbrCE2mTidQU*wJ&Vvx!7;9H{dlMyy|Rb=LWDy-Y{?=#!vU?LI&3Y+(VEVip3j5P zB2j*8+Tf(jC8LHJ3k(Fz=vT)`1p)hiv=0Xk49bWNhBo8lAo>MKO!D!EP@9HTeM+rF zkli1$2lET9VPXYp&3odG)dkW1Zs`jW^%(cwGMI<-vWV={>7y^b7xg>q6xV5~K@Xx| ziLzXxj)`vk;T)zAQ$yGZJNZq1B-+Q|?deJL_m8?hd2p?v>%d4GH-7VM!4Zo#+>JfK zlH$G}1Z0HP`Wq-IpG4V}4|bKio+G3oxC@5*>;&%}XTCVki|17`FggJUTYG57ziDC- zO`p6Ky5eVtu?-L1pYQtsCVB1zBjDs$Y@K;`=0Pl@UM=XgZ=KO#JHKUwqo|16oJXb> zSF|Z!0aTE3k`ZthA^3$J2{N316}=S(Xo%h<`0x`AbO*4xA{anwjJ)Q;A-DQtm5_fG zg02q&gw2TAE7cJ^y;jOEUhBkljYg)Cr$qjodQLhs1K92qcE<lC6RhGzhrX&cY+0lc z`cdaw*HEqkx9SYjNX04GNdXo&Ixw!w)OXq<Ji!0g1i{b0xG~M{6F8qsI9H9XV^^;1 zU#V{7eU_2M90wCwAIPRS(9pT8R<*rJlnz(;6>)hzip$T;ycr+{SOaI!A6qn2G<25& z54;-?&RNME#tc&*q`nsjjPV5IP|pCQE=hv|7s4|c$n3Do+UK&%QlcK?yiy4bDAvC* zGt2?2(I68YM`;RtoIZu61bY@tY>kIq4b*<gH4n?n@_n|xIRGgfy-;kIP%-UcQJ3<m z`}vqc{uS6+*RQXLls*C=^|~J+9l>iNBf$$aH2w@xGEu?B>y^4@J^et-Q*Y!)A))7V zYn~%caLV_=xSo$Q8}h~mzJ2Itk90|~z|Z(r6Q&?Pct>@!o7!kSU;U?+%vOP-O4Lf( zdSH2DkpV?uARu7i-(8Ji0@&W5zf)nKqDKC1uwVYAR;*7T6w%dr<f$u4A~A@5Fn={z z=Xv9T^4wrI8~b{e(}18-?N5is`$i&14aolcv@gyr1z%5CQV{lyLy^DVinC8FenQKd z&uUJKjhrQH-*C$l-AD-?0_!Bv%$L=-yFphN3$e9Cly1j~VZ;B?7p|pgL8VqM<|*Un z603%aI^oTwKfRfvL5t_;J%=9w^YJ~~;y)UUBd3McD}raoMVgkSW@K*tj;+M`&0t#F z0+$XiKq>YlX$s^qmAf`0wl;{m+K6YMUg+bt^f29AAT&#pQ9ZNv(yQI-AGs5+gOE<i zPclNoPvV)=OwD*H&7x`eDPh0}_(^B<>jNenE+{VG?sm)vfw@01?7Z`_7B|~FrBly^ ztTy<$NNAv{N~Kp_L)peS&seBc+^Q#8=6~MQu4fG}(|sw-JV!)*lUVhn33?C7F=Gpf z3tuz6#ZsX9D>%Ny028$}N|Inl7({yIRa;g;KBUanm6N}$cy_b4DG@^X%U&pkl&*~5 zdeXLZFv+>u7j5O?9<NTj^}Wu<^Q<*;fK)KkO}g)z(RfcPU%Z@y=2@>saE)K7Jf1;2 z>GHU&lrAi-J-q3@2Wa`kL{@F3-|^qC;h!c_8Bu)l99Y>OAsjDzT<e{XJ)+wsc(JZT zz259Y`)Q~lPN+~CwA4qo3y^BPLJjdyI)miv+>{ArB05s*aeetptv9QZ_aJ>|>U&%D zNi*l@dkIw<o|k!`A9;lY5^3(QP5C+NpP@kFG0Sy+^JxW`9~9?vm7)cRNTB4-K*X8v z!$siBg_bN7K><=_%G|OI%z~m^-%~cm;6Kw|-!ZPdQbp4yZzlZ{Q4K)QI{o&LGJIPX z?WM?D;V#@@wgc}29mXqx=vSlqF;%sBTXBHEW!P+cWD8U{-IY>=bmgggsiWdPIX$9y zd;~~!p$y|~W97kcw@xu!Xbssa<FHeGr3R#ujY1O^;I2YlD#3V0L?+q-8}o+K-A&z* z)X@!WLpAyM5~yUr`}kZ7M8m5OccggQ50XYGdR;Xbl|+wQ5)urX-Q}c0@V^@DcUNPW z0M_p{n7dfZ-wlRm$As11CfA&#o@?w$p(Aos{^v&9|Ic7Oco@pxy<=COb906mk4uuF z!G7G1iI0uueQ`esX&v<Or^2e^HE-9zUSp#xzcprE94vIY#Mx`!`;mqm3R}`ay^pgQ zu+kp47~8iQWpRUVdF$9F>82}kemXdZMi>D`of3e`g787A*7<=6?b`(8QIedZa(oT! zNAcT{#5CxYe>B)6rxHVlv6Rwfz(v-u4h7fCwzKw|!4S;5&`YFV?u%-eTQ(p?VGG;3 zjh(%lH2n2HG=nB$j3!oKa7*}?n0eQ&T@0b0uKn6+dcM;{G2d+03d0bHNbPT_zeVY) z85}Pbhn+&@J<5eTv2?~N>!jRw#{RT?%x-jDZQ?t###-gLPMNt@&P%L@JL9T?hI9q} zRV8nXZ8o5498K0l2@+tY|Ed%u!U|HHDDzhQ>@6#F9uQou_nPTy9E1!qFwU-Rq`gJ1 zj}%U^bX&Qqz3wg2!=PD>`U_rZXDt?7ER9?ee)XRwbl_9X7!M5NWn7ErU}S289afoG zhVaYnwtnCOq(<jcZ9I}z?~EHO&6msAi?^dl!o5=W$Y#S^;Ly1ATk=Mi1ju~9`h+pI zGU<}aZ_5#Q#u>$8eO)LW9CDG^Zs8Ed5zC5Pi9sKOLcwiJhDEED>I^_Coh!MJU0;O& zNKL9IQ1Z;^D9NU_*}eQVxEhGQVf9MAnz8F(FFaSL&a&1qNFeT*@IOH#!qS><`Qaj6 zz77N_{0>SlPwBnGv?k05^H2Oe7MrW=%pA-DjmwbjQ5Lm_1tYO(W482cfYfgbcN*1~ zGL^2;90+hUPa@ytG2d(rZXdSHm|=Jgk+rB1{k{#;tWghIvM6cvKw@1tzKv*SCrm;T zG9!a6((k+FI<6Ge@vo;P4o(ED);!y~qeLTl!2&&FnTDt;fK>6EwmB8Pj?8zoxS#Wd zxro1Gw7p$}(UUdgf6k08#l(i+A&m7k+C=OQjMQb<kKifAL5%}TZhTSiE8y<iD0tbZ z2LZj=w+_c<c^{)m%;BK|MRz};^K9-J3>)@?@6}+xyBfm;Fn_PXobgruZZPv*F<EC} zQm)2fU|IfU+8p2!2j;&TY}t}qsVb_@d;WA&j-L_Rc1Z>Jwy(3p5NXbuzXuoe3>ApF z?{bN-$;<B=HK_%jy>hcy8()&3lfL97#-NUVmu)<Ry`o^U@k+ELD_ah8I|M>4uxE$& zg?+LYrilpuol(6QakoZgXFi@z@ByB;no4qB^nua~a0=^F?rMOu&p#Th0a$3dpHyH; zk<j#*?FMqiaq+eN+#d$>VE4n+O(D95>SC@nz^wmZ+kjJEQzmrECbTBmVenxLu8_dz z>}k?~IfX62Sf`_@<jGd9Vp`ub-@bcyvFAv>Oegm8dB+*MS!^!MFEw0HX@8zh6#pYh zt|ZfhI$dEMRh<7pEMYSlIv&5ZZZvJ%jE$25f@rGD&Q3EFmgG&vdyZsxix!;|fSLYS zMA@bU5%ueWxK7C`D<ZElxBBfKC-+PVseAS4Vf(otUun!qfmQ*`OmD<PCSIuZU3qsC zcg_>N@&mt?JWL*l@r|tHhd^m{mhl}I<)~o-jStyAc+k{Bq-=%Y767Tz8EyRY+62iB z2Gd6(mda2g^GlwuROH!wmhqg5d^Jubu?0PG-FA_knu_GeR^4Fa{HxF(h#&4OckP({ ze0cN1*GRMTIw##hu)>XQbrd)<Cm&Vv3=SPR%NPtiCje5PA+TLsmEAaoWR<M@zgsE9 z{Q4~YN_E!pfkn?Sb!s6i@zxDF9;so^fNH(k@})#S3xz>eM3IWDk#bJxx3O8@NcA}Y z4gIe90H$GuN|P-J<^-Wz&<1yVeKQS%0Qkx;++n)ec<W>##xxeJiAB73!TZqkmFj*B zCOA@TYr~b6MelM(eGSh7JsbCea)`W|32ECCF7VFt+ZVxBJd-l}&je+aG$gqA6-!!b zvig|B-_~fVW~k$nmBAZ$=kx$lv#e{U(q%)JT-7|c9htZuh<y}RU#WK*N64JmWX6rq zc54<VW+6_EvO5S+-zI1l8{nUlJ|eC}n!Pm7@bNF&7rLkstvgYPa!W8OSgD_aR4eJb zV>)MohzMYOkdS&c*zc~!FaeC;YcP8r?!Oz%z`GxVs8DmMa*4&;>2?u9!1q4<uLdhi zq#%E2-wM81+ZTjqU*lq%&`xDwqHE9PtQuoCBO!3f)PZjiIM%^f>{?ArCESL3A!-=y zVX{U($-zzr0VSiGQOi*NI=87EeFTfmTgZv_y-1Bi;-p_eBUBVue7p*@`NNRa8ri3i ziUhe<28DuErOONcboMa35sc-#>DrTjG+1p|$x{)J6?}E?5J43Vw2>Q?{Q8^0x<UEQ zvf?{rBcAlWQV7=9>g8ANDZhKKBEe}Pzd=U}FBQJ1=sndZf{vq%m631XJ5#<##mqP` ziJFp>B#47pm6Lkkb@~0}D5~_^C8VcA)_ZVx)en%T2y1nWWICSszZh)fzF9kGc6^43 zq&!h2{MlhEaer(@cJAQ}Tl17?p)l=7J2*;4o(?e6^FFBb5s;^Q+T6;JV{B;aRGO=t zzGk|sJcQ8YOGhAcvqTO!6HFF!mH=i0TNLeABWI5O(uH=YiI4Z%muh`6x^48dZ687| zOUv2n;C`qumOD-l^#likHR^b0>okf0q;||V0Ua_3N7$>Tvz1i0Od`W^1iVr+*f@vV z(_w=e;#TI`KO0oMs2Eg9LRKF}Bq$=A`hfMq>oaf^EoVWAVo!7UZ+e5kAy#AH>$~T; z;&C(>JR5UjeyNg7Zw*EQNM-ldL!%9#xc{6_@IIa@Dn7}%6!DeH$Z`G7hl)SrEQk{{ zPMs0kic<Nf)O%R^W=cZ-6K3SZwV$p2qIzzh56CqhputHPo)6lay80>!w#E74$ZPho z^%S4)%z_lK0H0woa1{feXFQefdnKyn&d41@i2|=wa<eI|DV%*HzFQQ(h-UZ`;p-=5 zb#?;6wk=koD8vz^QVa&-`P8yrjTOmKo?P6}K==c1(lO4U6BHE&9N#j`Jr{V5#5HGW zfK=WLUB!aI@xVU6MHG^y*k_giUW7MqR!Ne!CA6=?J<nq^Gt#Yn^K>$0W>*DeljNC@ zLU8Vs3j*7xod~6MxyFj+{LUioq=wW)`4XwC)|GI3SY09hk_MW_nXxkQYOvp3jbQ@l zzt><^$g6)hnBBu%iB_Ld!5;%S&NX4yMm$LRzZfj-sKwFQ!t?zF>c_s}t#>PimRyIq z{dm#+y*u$j<9zinjqOO#w-g=@M(Q=aCD4jQ-z>uFMz9+~1ZJ+$Nysb=FQKVoYHC}{ zywrUQw|0QUCen9A>;yG;wx7SU@U58ghShKihu|uQ(rvIJiYgrmz?MI%mOR8e$g{wh zxsE*jqruXr$r74v6u!T6+Bd_?Igf}XeqMSrm}e@{_e*3Kj%~zw^BwZYZ0+_O!KU^? zbO;#%bfqre<~!kq4iASNkKqflmTE12_bKEQZ%*sLjoYpUxf);;hTdV0JyhC5|H~Ky zPiiwwZ1?!OA(OKoT9Tzdy?!y^_q9hY<9-t#g>&s@Lb5~mO|Bq{q3wtbq0as=fw{Qa z-C-a$B5#4##{n?YA&;6T1HYgcQl2qBANHL;={&-?y=MC1!#cY>l)jA;d7x@@jug?j zb6Q0egLKhYQhfAa)wqt;1N=D7zyu;n=yL~!RUphbZ=Ms4zCP0~dc25GoX*xz0X#*Q znIm9>1?wodKJd^!)KOu|G$0-yJo7M3yizYv?Ufxd7V5xnhp)CdyL|fsdLmdp;7C<c z=@jG*qn2{9DGVkYx0>HTR3lE(C(EpcqEs|Fe_6dPPGDx0Eb0uJvc;RSvHu2;%Ke?H zmLN29C`K{U9MgZ-82R$f@0AL+$q(Px9E?-vdSaw${FLnpHUlzfPeGnCkn$`zL)eNF z?DwdXYC%*y-Vrj_5{pMnD&#?A70k?MHTR*iWk52d)-usqJB1e@HKd^cq@(A~wl5=T zO{YESs`axJ{wwvV%$2xBf6?A6(jss%tn$41;aF`*a65=oq4%YIpFf$Ye(?PQo4W$x z6K_VMFlM)P@d48zqfN<nyaXSs-gz<?e2soGExQjuDxP=7J|E;Rj?}^wD?2|`n0J5s z>wqdLvX|{TtO9IQA~f3kaB*LR>bBuLt6;;Ac`kRf@CAiP@uz&aJwetovu@BcE*++l zDD3pv)_~XI=85-2L0X!{MhXd0rT{SItHFMEHHHbG{a%BaeH#6{!Sok#nhG6c!Zom~ zIDJFM_Y*hWV*YBdUad<mkXpH2;!)pF!m-4tF*&E$@wOFHh81Q4Y;VQZ%S!S`HC_BG zo^xM)+Cdhzmny^uGPVMiBi~)n_V@k{DRJgqS#c6@y~`W-G5Qec8MmauCQKJ(qfbmw zrMFeVSY5D50o()42nK1DX#3^pYGafRgIw!WF&E3WKczhi|IuIy8pa?-mfK11Xe>34 z$0UNyC^<9U3^w<PM5|ixeI}SMkOi!tdg8J9nv^`Q5%XflrPH%%1?U2LE67%+I6D@U zXh_k!BA4~KF?xP>P)yQn?dc_MMj+U-MO2hQfd>@p_FpF?<EF!kxmZ9YpIE5B1>Ew( z<}C@vT33E7?;xyW#7S+eV4BJpjaHy@DxtFuo!6^ZVGPQvc(+KIGkOLv(^u*~!6}S< z6l%<j=-Y}kIPgmu-gwP)hP<NS0GASQ8&iuFz1!aT5d^1#fX9asMygsC%tG{(G}r=Y zhKoS{n=DQe<6$XV51pDk1+*}UPkH2s9>2Kl5lVk>zdJ{o0!Wp+?TC_H4)Cqn`s&7E z>3MZfAf@n1r368@pH2~1)q{KHnS;wQdrzY8aDh)3&qp^Ui7lkP&GX3uLLN9pmVh(V z(S>S)Sh_v!?v4YiG-|8*c%6EfhFYBM)T<f~@SGp=mn?7RPa18XPqr%S*N8<%7_{PE zsmpq%=gP)*l}vuprq@tm%WMf%NeKtK#eVagi9^$LSdUSHoVs;!&$nHI9-tvM)2lxT zCw7R#9L-BEorGjYu`N;)!LHIl?g3K49jZ`H@Fr8_Elk*U+{7$e6iUiosjUau8=vhV z4YbR0(QcX_ly?WyiSzR@*qfMEMd#Rz+NII**B`9k2TfNkaf5!hJJTxqj>}&$?&+>? z*(y*z<_I^2a4PO*1o(Q>>LfaTRsOK*4#&xAcFpCTl?&_J@AU}>7*bt8^;o6czFZs} zE<daO%o%Ha>BB|zxz(fgeA&U=cuK*ko$z4x=A`9~&K5UeG>VyeosQo10TS>ybxOo~ z{|-eiNbIY@es?v7384O7gBfLR{@q{!Bl7Qwzdf-${;D1v#GWphG>YB-tHJtCv7Q_E zWD99Ms5omYEYGvZsGAf>&P;yBPcZ~{s5_}EmvnFM`d@21Y?=Wvxv0X;2n;8O9va;; zIh5@CNdIV)em)8kRJ1xhg<jSIkw=u8P+@j(`H}IU#jG`$sEEX187;%+B4(L8?N&fV zFS_8lQ>p~CF->1iu;ti(&hPq<2FtPMtRLO!INlH{Z4x^RM*&R_nSL`EGLYo?HEe9T zLG(u?DRVwXgTM^$joIET;2o~K;-*+A!RAdUZtL-lIT;kxDwIKP8d}y4zZ*24o#D|o z_3IyuD<=l;jH33zGk2DZW7-OB^zptMq;U_$X7f{ftloS9irFWG`!a~B!Hn{uDFa-! z{(Q|j&p}QH$R%)eV2xET(m*|m3E0_z4Pd7GC3+Q;SY!U$jtkx_(KC*YzAL+U&Gb#7 z+|5DIUkKD2f{|e|ED9MD`do&Yc|5kRgsGS|6Oh~0o86aogzrtQAMjfZxK>Id&yF52 zqcW?2r_G|^<$S)O_9<MONFM^E0$U-FPM*E9%fbB;XLY?;KT(sL`AW^^K|*m0{j712 z+P+eYGZ-7IY-qwgkY_LozD?`NIH|Lc^Ns{)lU?xJ#oqq-FJ1#<uS6g}URcx1a+VU8 zZWiH%#Cznaqewe|R0c9OmG8qkK8H}Bb!g`ncGVAdjb5qzal@LE3D<tjVf$=^F5k$- zGp2=#K7h*e3GW!Ey!6d>f&(+PL*glKatx32lP&<=m1q|(F#~reSF1<tqs|taLhkpD zC&NPlq<*LYd#E_zMT8%-*8o;0o7YWMIew+$bJ0G=I9;4(uekMq^B<zr7O1dQ2*sVl zmzRVpUyc^tWJC3UVzx&yfpw_pf$}~P-6-REnl4@tv>mdUtCA|Wb4WU8m0%75q|)Tz zjeZ0#3Op{L0iM0jkQYYC(0IE~f8bk#9M{xjN)8|=oMiMi0;BJ=tn<FSrq93=U3{>w zN(s2={{e~Ql|D|j-yn#^Vs6SKA!F+&=Ou%(DlRsPvpiUL9*J4{YOvp3jbQ>Pzt>=T z1zdkO*j7*%Ix3$6G%Z{pe9<N@u>q3Z%3lq(vW_F@M_0T%DiDw-@su@e^yx#paun-C zeCoGuoU-lneFC`8<{rIKia0n|n22-Wu84!bY~Pz5P)M+p_Ibx;%;PStbu5pvab+`Q zDdF5qMcYt8k8r3d3-T?iggj7p@u4Od;Ja#PBCZ(<kbp#otI$2Mo1fx*l(}j5(1<5Y z{6~YGP3XgSq022^ASSAjAWDn-#w&2X87%9%PYwqciSaRGXRY9)4PBYFo_yyAn3STt z{Ll<zb$fS%NGx{{YdJ#DoONnD?X^)|hOK?1XYDPq%gYP*0prF5ONK}o=;dqT@S^gT z>>s_!2@RUP@^=?$5DZ<>DK!$dCt&J6{45=57fIg~*V^a#EKb@&GNK0<-0Bz97j3yc zZ>GgsX}$u?bh7zjFIo5ZIoquw6z5KjQPl@-Yp<E^@oaS{ee@yQYp$g{LjFtXi+U)s z75cX(S^jvJXJH%>LbU`+XWlA77nn}P_aY{hio|F<+Nukm&hoKfGmOt6-aqP&J?bX} z1EikmLW+4><0JMm-tAvp-`Ie(486GobnHs|nA-`O9y(QG=LzlTW$<x*HYKf!QjT2Y zq_+Hq15_6LNFT4|NgSqRR}{%n1&Qi25L|0GOPK|RT5wCN&4^7*Ot3xR=LZ;+cN;i^ zNPI|$4A>hPx`GUG^<1x1&joY+s&R<x%E*W3fq0coX%P@y)+UL$1Ch*Ss|0@u0~C*3 zGHG~ZR9aey^2mqaeX2F7sI_`Ta@=t&Ec7$f&`E!~{6TmMfK&sX41dDA4X-3r!pg>4 z7C{w6Zm3u4yhAQhn5pn}Jz?fIWh-6iy=jY^Jp7L;k*<)#{*%CupJ5UC7Ha9ulE&bA zO<jw`R4<S!rOtZ96DGX<D&n9F9g!q!tGC1w08;NrSqp?xypo~T3^sU}dZ#dRsX|_< z&;!yx9a)wuw8yr37%MudV0^KSG<ij@mRhq@d3t9fQOJ}S<ddL|BR>Tm%TyylrOk1r zO&~yx8uwQ0`sgP?l=AAOVe2>1y&CLyS7Vp}^6xd6*24VX4Hm{mU9>O(Kiv&XWVV29 za33ap?DbcJ)jcy`Y=mxi6J&`kFGSt&#JOv8s_WHEId(BM%0+m%z+Z+)?i3w!1E2f~ zAUwbSW+b=^g^|JWg{8)84|Lzpt${$}P=b?uNd3}<z|ABIQ@j$D&abP>x~^20hwvi( z(Fmpr4WFXLlB+$MVCK=K+$ptZz6Ekq)ZDb~V+o>X%|9Bfv`s4*u5;ehJ&|}4RrV$; zZT5Kn&0ugn7#l@1{51u!v$&MWPWs5UozJM<Gx9hOjG_E!&G=%C!4n75Ku(L<6f-Rv zb(>QkX<M@f{9l|dK7CR)P)nxVeCO4j6RAQ#0|Cs~DQO#GE*S{^Js^K|_K<yUmz8{5 ze@8|@6ApttQJ03XJh8d+M*1U7<2yV`9R8>uI+Z`@RiYrgiO}i+W;(5j6nH%0=Ne^J zPsu6_F`$Tb1jE-%cS-6os|S06;t_#)3YFWK35>Rl9~elYeEP|@wi@s(p&Iy^luYjk ziG!UTMC<g~*}37nw7O!%;*TAkuQqGu1%vdxoR>s*&VXlWmPBiN*#g@7>%!FWQ4|xR zGiTgisc<5-`kGeNXA+mqwmjT*4exf!iWlKdu6etE@SE_poA4>ke!&cV_?1s%hHS5P zA+llbLL>@1iR*LyEv88Az>Y=6fbNJf3mG6)(le??;*-N-Mp&x}v_t&mmUb%d+w7Z# zfOb=-mzh7m0IjHAbI~$SS&qR>_{m_{eQajmT|}JGgVZcM>_qCGCPV5x>gPXf=?M|+ zKue7WbCdc|mnL$n@*yA5?i3*PAz%=NogtF%`lve13fqPOod72Kl{!+l^S<k2F-db6 zqn3{%H#n027&!t3Y>=4AyJww-R1%luz?iYvA=hu>U0gX_AlV^Mci^{<n2v5e;}?f4 zJ^RgG;ZLlm<y`=&peqVJR6TTB*%!NC6ngqSM-zU&f2BSKsqH4Ljw1&L&TYjeVT!8F zs~SrsPw~n5ebZQzGuYp_j0ai#?$y50Fl)5u#eUYiQ8Q);Bk7aGv6Jb??F8RiIKsQ6 zHe&H=u-{#cVFF0M*I;U6;eR)nU6=jSC{wRTm8xl_nyuJg*Z4;1Ukx?~9Umj)3qB>L zE*lY&J)wd&@~Khg3!*FZym=G{qq{B{!CGb+B2EF4QCm(vaKIO)ZLa9JAyh6N$5^Cn ziFWN6JByUfX<jD*{QaveW(X&zaWn^Y!cezV)3u~~ia5ofpa`8&)L`Qw1P#H+X9J-# zqbq`X+;<~POymmSQB?o@Eb?|g1?zG<JYS~%<YA1Y=sPer)!3WCSZ*Dhr_#pXEp2%j zig66Zo`tYRxqE0XYRyZ+$XRhvt__aQ_es=MTd-hdQf#<rL`An8Q<qF^O*ELWOXUTt z%jyR0FFHAV=WYy>icJ9y)+51mBZ-iwB@O3u^O)XOSZK~Mleq68Vj{Gm>RFA1l)B}C zDRTWGX*0*-VdIaMxO8PT=^)<V2r$#36PtHPww@I-Op*>kuCr)}r32sANC_9mgX_q4 z&Pu9}g(>4aZ{0nsADw^RWWCsaIrSAGDw>fWtIQ#vNX?VlQUUuV-<Q~1H4gV&(U;OQ z+H>=M55Zn27c!8^VH_ZphX^VS^SJN&rD+jD(XsdX#JEcCmCB)L+c#z{)U9ll@NuOD z*idua1d3qM;G9i(PG|$^j1oq(Q^{n9wI)Y^!e8C_8HwB-Cj+9kd!-`}m!L0;6vKV5 ztuEOYYY!k5GjGStBU?s2ej<pdLAN?)ClCDH+sct~!_a;ts|n6#ZNIA~kTVeCM-wSD zsnSo)gK1_96-mv5cLQfzxoBMpFhNAH%My`eOs(j}p5ZxRGgUbnQBwx%HgIGt2Y~1N z<|bJ<uQu-!Dk7qK5f56V1upXQUa2gJu@_Zp@C{#44ho8o=6WdIowBlGUpTQGv%g%& zefxyh;;`z=;M5w|bN7yLjGe}tcoB4jb)bn0xm>)zXX`Eo<qSXCTsR&e6<d!pVyW%) z?h}+6a!L;=W6T8b!Yh@Fi#kwju~&vD$Nbu=26(RJlQ(0JS?*PzLJ=poSLDF?i_DQ0 zQcXmzA~JI5t!0*Fw*ectJg2`Yo$eFl_nT2kVZ}+;1FEi9gZ=Jm3==^7y#`Yj)%d%? zcy3|cT?gdsJhg}skcWY{g0RH@<r?gs+%Vh_+nus2dvm?}$#!u;kZ`34wm@yyj1SBr zU>^IsN3qqt2W`Ih4_V+EpL6EoI-_SBQI2bKN`ov!;boG;{b4*p0o~~7D0lDCp^fXr zhtA83ihEQ~mqMBZEfk(8SGMm7O!q2XQts#;2LV22lmhPpBU68DP~IshQ_<l5qrvp0 zizIlCk#EzjV}X8oD#(S}!Q{LdtX~?=AgReZO{@~B2BtDvYYV0ut;skp%4mE&f_%g4 z6k^L?jC|n0$~7m|MQ{JzJli|{O^^O_elDpi$U9p>A%bS!i+p?MkeaNfvV|eh9;d5e z+%+ry)UZG<lL_VANtjWkccY!HTIiSGO_RLL_;$Me(p}R@x7eI9@LITdxBbZmt)jpM zXaQ#WDbysc*SEzNm8MK!=IP+P00X9)*GzYcV<*2c*8sZTA9jSm_WN1msc-SFx=46f zNf@UI8PkYaQ=D&5;-$8GD~}sU@M><OXM_J)AErTD_Rx=n*Jh6L<`S3Ho`41*m7-5| zt89r}dRUw~_T|S=MR}a==Gz*M)F67;pM|F1XT2?L_fH!v(*4d6CH3IU5FR!sgVj2r z7nW(~b;cYBqgojgCkh23kkf#HTfDEROBn+WM$&trKKhxa6ysC_q}t|M!*#VFb$CNR z68Y=)ym<SLAHB^Di6FYfB%yeK;`%X@+~tSXRFm0ed%WYHy~qHG0`@|Qs&OYoV-%~x zaiuL7gl4BE+NCHVg?N_gXkE&$M!~!|-Hqd3BZJfgNabr-s|d7X=6nn$$kDMBHutX} z|ME&r=dn6G2F7>5P3=(+@VP#Bm7l=dvxw=4mY-)W<fq%;JjN=C%OMI{UzHm#f0%c6 zM^b)DCr*8F{zB2^K$kDu)P<~*)LU2#kV-5aF`rY8m`9T3`$J<}7FH8T4C{?LpY#Q< zF5Zw<USOGgHp1(}WloqfBk?sVI>+}@1-A7Zp5f`A^>^YxFm4URKSBGFr_mkI_nC63 z!yvn9-Fy2a2$z_PB~2_}4feaMF-!pA_ZsY@{Qln!=3A*iJn9%P9>`1RLxv*-^T2G< z@K=Lnn)o2wJ=U&iJGVmKKF7qC*Y1vA^vH9klB3~Gy<2Th)+|(G&T(2YH^{`3w3-SW zB7o;;doOHV8;Hvl9h?r2x;UO2leB@6$*k+6YP=WRmx^%|{WVKfMA;raxTB#hZ8IWc zgkr(iBBF$8V~0m76Z{U((6M51r(%pK`z+_5pG6wQV>}BTd)?B8oUlg^Gm=tMPu#s3 z%s&ZgY*nqeNh^J^NuyMHNRpLHAbE4$3#NmnNtv>C2&L4`<cOFp<7@Gpz;OZ66!X)R zpk3G<CK;^5f%=LD&#(%rxm~r;<J@f5w%C5l0iAq6@}z%u^41X?_f#mMJCSL}eqnYb zf8mr|#d~%Qx0nU4Lnuhc{=la!PZo1)jpQa+2It!pz$*;yN@X)6rNc>Uf$5$vHGr%A zY6su05G1FoJ6$2mJAwPRQ;(iF#P}Wd319=jL@)#bxLODsSy~$gqZA3e9<i(Y<QS#X zAn%q8ii8L<Sc~KIv+wEi>=!i3uyVg+p#wHpvsDFT`Tn^FU;aK=jz$ot?BL+rmESa+ zd@ZX*Y93b5NYI60#w4ugK5Nt`&Mzkru+0;R2ERlVb+`ST{6EgpQJIQ2a`N!2pHXqN zRUWEJtMXAE@{ljDxW%cgvjC*#81jvJ51Lap&kAQSR*4B`y@b5p=t~~3UWpkDL#ye6 zgM(iSaHXJF8%R=rnaX|nY&n`y!8hwZ3wtw_UtQF%m4N4&fGM|{AVu6+F4_ojC}f7K z$+dgKc*)Lufa?H|N@BhtQyM@;z`y5u#@yv4O?>l6_)2AseM;<F6_buo`q;Gxhe^qL zuA#do(QCV26VMvqRmY1VFO_geeng7F7<yqlBjSyKB1Zk-AWieIQfPE>ySFE>T0RuJ zjv)qk4=8^@lN@r;s>0|+oNCaNT5B<>!7KIB3lFNEbrGxsLD<CKdf0kXgvexm;`o6Q zNZ5)(#RORZgN$S70u_;V4M^;1$Yo{1cOSp~=M9kUMY-EKq+6Y8vZv)>Dmcfh!G3o& zh6%v`UW3WDd;i^Fv+@uaL8xI&5mPwN+Fz2*qZb2!{$(D_5BLcx>8I|j`HX~g_{Lr{ zLm2H%iIxa~zjbT&;Sa48u%_>jL+NZ!WFlAG2#9P!TjOdQyIY5{on=YHX+Y`(n)C`e zxeBd%Uu<uODSz-b#So8)f3P{=MW4uJ)S!6qPT~5dUlkOx=s<M?`S`)Xbu{v5J8?4n zr?>__21l>hKN?KVc%MH?s8f3ci|%okgp_spy?fT1!Nweb^wOI~nV~C+a7{<>_U>E0 zv@`6E!gNhY2rJ*-Nr;}JuhFYQgtj~wwBKEtPas}k^fQ1!(ivYpG}km!ZScy1su#N9 zwT*X^N)~u8H;;c5O<4C<C1T0myE0G*13Pr69R>2c3Lb$R9^UVb{*EeG#~C+wR)WA_ z%lpO1An**&FKqYP?-pRDqhY%%oMs5awA;HYFW)&*=<$8yf6esx{PY{d;+18_vU&Sa zA7g6|CuoWbd287{nv4`8teZ*k6C<S=*asB8(Aq%$`zd_`on?WB)`Szcj@HV$xR?H2 z7MKq4ek~n<)K;+Kr`pE+GMiC1d2RiyFBU{z{%^Z6ow}4YqbWlXB*DtR2<1;+Jq(n} zt0JO4#-tTWH8^#F;TV5*pPhJuEXb^Q?DyOiT-ryfeAixJ^=^JhpjfA9O63DZ)<&`t zK&q2z#hv%vvby-D((w}?HWr^wN$M-rFG6eT0}BC|2vhlBHTXMo6rcVBb%PD%5;|^= zWPd-$43+b;Ix><9$>rmNIcpz`O*}}cCPm7u9>?1~e6*2d&0Y!QioA4PfK;vnhnSd( zDW9T`U$5L`oCrIdKRCVZ@9`a7fhpGd==Ru%kGz1u*B>)*4a{k#wosLCZtH(LRf_my z?_j5JW39k?v4N=m#-Js!DUqX@7P4T?b%N^S#j){cYIjNF6@b)}GMFFPCbE3O3-0Cm z6J=@NFMnCQQu#n^KD!4VDJVvb6f3Fioa=;!@OF@9JRb^QM_OHG8@5%k7fwF0o(*G} z4Sa`8dmzq`MdWM25z0vEQLotj3Nv&v5O7iIeEBvH_M59QOaSip8cZ^n|L+FlKV#QS zbQSmIv(;H5tEkeT_d4<YtHH){1x8r|I<~iOCDR9pCUf|`f-ABopF7i`j*3<LE;?hf zlsc(Uk!(Xit9|9F9el@_h#yW#J3L|5&=N;7*yHhZ4h=ymCRKAoZhH|#>rD1oSh)OU zQI&4)Z^$=*PiQoKY!la*YbZtr?>dYdYq!o^B)k&4@oTQAf$)H8to;7bV1%AEtqU2Y zY$r+Id_qn$AHKfFnRqi;0YOm%56IoVa3TUqOpB&M-d)$vG6A;KYqs;Uc%v0(&VX~v z)MbW@_9pg5+QJT3om@%;G6*8y?+3qHunt|LP}cqArY7m|Bn0*p<K&Osy2Hz$)j%f~ zOp8C?$XJ)j#$)($4o2uHyVaJR=&|CD|0;p(FP+4(ZnE9QwB9k%X2Q!+CvCuK0NleM zk`o6L0pDKT8K^y_9X452>(}#ZrejfP+6IN+(BN9-Tt^lUbYxL48%@;g5^(w0<cmF< z?O%gb6YVbaN+%=N41W}wF%oTUqAy~UUbT(c)?Q6hYDaNO<jKsNPz6XGA2_-h1?Kzt zw5`jsg_<R-PQ|(ZN?q-R*5@5$%K_i69RKu;K1S(EGQ&M@ZDXAepU|ihrL2we(bUMD zvjtI*LFJ}80E21q;|N-JN$~h(NyJVD*=jgdSaPX-mKQ*3CQ`Eg&sasZ{3Fh-;o$8= z{n97wH*aD|0c92rdIEEG#W>4#&nC|Ri+<sGZw}u%TBRVzk3Wfa>0_*g1dotsDA%?3 z`Q@d)qQY_BTaMLg2eMBe#!m;&Qh?2g0q|ZXPi9rg-dS2hn>i)bUWcAg*l&Gr_vt1@ zFFGydv<7uNA5SQc!v|JQK8w})bXCn36+*e&dv<0E;9mIGXhw$wU?cM|YOgw^cB~Ta zMZMGNa4<*<K{bfG)3;tF>(>BCjr8;eZlo9$NM*#ZB{r-%$Z`*TTa%BmMoN0FBYe+Z zzjcK6ZO$@QY>ewHDG&2b`jA*NGD5fPL)I6YoPrgwb%z^=;eCD#e9Hg=CLOs8%dgHd ze06glQ{gE-rWK368tivhW0(N!?=_fcW9Q!u7Dv~M_Z~m<kRAj4>Jzr5RVH+S_FoM) zTicc0-rEkl<P)G#$y5>a^W*$a*+X8#$TNh?<3o;=T@$2k-zJ9`BjpI8YWD4A9M4p{ zF`uub&!1?xy-9~a_3}tl5D<su*Ee_BkT_jJ{7Gc)&)xIvtY`Pqc3vRoR>-pXKQYL{ zIgSeOf73PE4Z1dS24yj1(3{%ZLu>ff@Q()b-xDD4T3YUou=~{M?^Uo7E#ZLwW-y%> zS9~E7ogg!E)(Wi8hD4W&?iB?Mu09M{{w8t=&v5<ZoEV|t4Ub>ZF=vDqX-jWs3A=A5 zJveNYvEW!Va^l-^Kh<x_e;&{ETt6rIYCkiaZdzJN_l2NrGK!3wcZ5>hUr!ga`UB9S zUDFP4l>>e^1`IDD4w}Q^QbO7nX$OmesWG$#dJn+6icBGuC`)$SYDru2z;6(P+1h?) z2)$-{6T>h^=yUbWq(i2Op|j^k1n}%3FvQnG=^%D>WBoJu2Ej*myc1S|B|$<{+p3Hb zHfv&i($Pji7%>Nl?#K$T_T-}x7oF1rfYiYpDvhCn3J>G{?~_G0q6r<{s3)(~6|6ls zLyjy=MG?#4kS`oxT}r5<X7EqI?q8S^Ne;h?tR3g$e!9_=|50ikpq@}-4Fi>&_Fg|p z=JU4<FoL<fsE2)b+pxw11c21Fi8cA$!1a}P7;=Ik4vnOzXkXv@HE_}hDXq|1>%lIz z`IIo+Ry}5cObXpKsJJE)GGZ_IUa0g)W<NhHq_9`9eoYvN%O(o2BM~XHBHFmItrLoK zboI&PhH=5%1AISMo<g>LR(kAD-7f~+qQHQ~{6hG){~*BsBlwe5KC+|pg0N?PZChMe zMbU)PMTXflW2grej!7Oy0of$Pp3uyPmoT~B;hRrPHpZL4%L|q3XLR)HTJ;Ik=tW$5 z^q&Ay^|HA??_!}s!za)qRYR_8XfQ#8y*;(KU)D3_^kwKQ9Qk#KSB+Zg`q^|Qv~?cW zeG^t!s3SV#9@lVhM`=!}aOeI0asVmclNqPW*I#Y6%QF1+AN2dmo4+<+nEv6HKMnS~ zt1(Of=Jy&*F!SG5Q&2k0TXaEW(FQ>{XoJrRC0%%bEB~v((!=}f;WZnBy(uq?S+<Y( zM|<s^EBf`086T6G*{<Wl?!O*l5!9G9hqWwrb}USmt{zylr=K&Zei(y*H0Y)SA<FVP zM4oc@0|gVcoZx0IVyTsCoa`+!hPL*c(qo9gEzw=E%<S3+>&T$^E*VmK34tqUz@E|% zPF$^SDWJ+N^^XR#yFQu6nD@b#9r8-I`LcAb1%}1^W-t_>sObsWsJLtX$S6I@v2MdB zwc01{8TBEwh9wfkb@VL~?fYEr4>L|KpbPXbZWW8QI;U80zL*B^N~>(T`g!RXLO`vm z7GJ*27>FkYr@!YANNE01^r=%qiU|klyH{ND<It80GmPmDW4Re0n_|JXs9s`hKc{~) zQN|@25vpm!jGAv3L?IQxOpg<Y=pBrR2w&I=x{8;i-yryLhW?uA_A2}5czvIEmaavy zVE2F%!WEL}yTO?<&*AXbS-|g2D>WBahy-X+{N=7V0u`wba_N|?D!-6u<D5*@q{evH z2ubm(qlr|`1Ef}`7~=G9l*3GN;;?@NL%t+eDFu3^ZYVBDHP^rBz&n-$&kBoPEBD&> z+=VWTH+Wwj4JQZ2w8iO+*x0Da^C%Ri!pyr&+(;4IbEq?Kl4a1Bpn7Y-K%b5S4-D}! z0i+I7ta@NVFu0wUm-!&3Ienglt_yuzlh1F~_sBO^6gM_OICM4Na9gzZtiq-QVj7B2 zL0nV4@{-{m>(D`uG5X9nQrFDUZtReL70CkslN6=K)WB~oK&rE6iW58o@LgmTjG3yG z6VBG+HJK(zcld2^m4D+aHIo6-F5&h}FnAuznMEMN!7+V>SzWiW4u=9KAQ8oo;=3GK zXc-E`I)M!0FJ{8k4||SY$sRAOLiKk-G_cdKg)0UiDz?540RXAzDr@JP6EATGpGAC1 z(MRv6>^{G(S3xssHS~i%3PgKnj#+-uLOAz>y6-H_pBn07`&*-*XAbU;VCxhHiC-d| z7sohv)(iw_dXOeE<}^r$wB3Ndyz{QD{!~yha>T2_es?v72|)i|gYhZ-+ltnRE|{ps ziCt#iaF=EQY;?vTH7A?D8tl^Gw)fee=BlXR5L~NSvX@yEDEtx<vHCiS5u}LtV<!$v zYX8?0?Z9nA<Vs~ewmxFE%$y88Z69GJCg?l1(blwBvFd)2@h>wk2+Q1LOSPs|#u6vo z#gFkE9hMgsXNhv@=03lkz&*zcyt~m^H!g6rw&AOH%bEnyXNt=LEa(5xV3HcSzK~6j zCFX|ZgI@h*yYYtmy>AAyc|x6LXPRCfSuaMBjoJE6)5Pt1k9TRa5h}iH{tMJ8ynehB zd%*i<f)3e&hFaXQl@%0Srf`rg^}W&X0~hHf8qDWzBOLD9ppziBc1K@^#)R{x#KY4w zM<k=*1IYWZfX@tKFT|TAyk6I58>y|_F1#HmiW#mDu|Y3#1qo>(rW>QwI^w!5fOk6| zQJan=pQ0ksfYm7mA2>$rfrGzhIx{`QkICzrj1arJL0b1pL0zsolXxF~h9wud&iY@D zk~R>E&T&yfwF8@L97<2}&Yr!%aM~zTsqG8W>NVop=rP95oRF<6CIG1rx}F4|h<t{C z$!M#A69fzyj4|S0sqABJUJGcT{9dpZB-19tEQ)s@-xFuBrr(JaSz;u3r#SYSQ)sDP z*B!RhVsYp_q46K)>U^~`-mBEV=jaz`8Ri(<%crjb!v;vjNiSVNnmdkMRS%YI$_?h( zr9^t0-EJQ*rrASHeH1FXJPEiQBdV&Slzv$!w4(f3^iVa~@@#<)Qe3?}E+jL1und+n z1J)ok|124iwtq|euJnhwxjr%Zt?@i+JK(jYkRG)4kKQlL>vk)NR_>NTn4<-+52+=C z=bL-1<Ml7?%eq8&@xs|7o?ZJer{)Jy=(LS&8jg0@#yN@JJM%1op)hWCYn}Pp$^H^m zz!`UB7~!Zw!vXWYbuIl#C-v?U@b%_6t<qJuZ=v88TPSI*qSiShXUQw|TI7gSrvc|{ zNsvH=lNIbD9}F$0t7_?1acWn8fZjLNeiCm7nGcF<CaV}+4x4!hA`47<Js<aH?aWum zrrPH_FrP#4R^!(LUJdrUt1(Of>h~IqQ>yFl28-Oslx;o@BUiQ=1zs~lwH~b5@cXO5 zj-k`M(RJCQ!k5lfMB2t1$AFp>Kxv(+D^}0755ee9REOFqZIU&4C#^#};#-i~pX!(d zH3$3qvW3h)?3?L%B$9hX;f2uPfu;yPz)60BOe?4wW)nBYHcly?$ag4VZ2j80NMA%^ z<agNH#E{mN`sj9~+JEb>H9!QK+~ub{@s9=ztM4cKxXij6TBH3L&KD12GJsI*&0s$Q zIS%|fgW_xKS|v^{u#;<)v%Z?m)E?cTUQG?)C?Q_fcOU~dP!@babY3HpKN;xC*Q_RV zi|iOEmQK&MDird^6-M)8n$!3AS`FHB$Eg6#%{{m#XEH2!#(=@CUkX*{$LL_lx4f)W zfL$0oLX$3tfFyG;2^)QxF*gUQZOTv*d1RBZx5fuB)29_bGIJvIrnP{oXalv102hvD z6Tfv%!0M186W>m?Wc1edb1lJIY@zTRu_tp%<CcaTSc1L2vhy91UHc)HiaZiR5bO0- zBUc3SotRpcNQlA&XtYyyKv*H?RWl*ro<33gFl82<_0zYAss<+(R0!dBke07h+6|NT zE$N(rSVPNJm@JjP*{_n=7T{bfGQL-gi5*G-U%6N=pPsUGMLal$Y3W+w4F}rk0-hQ@ zBoKa`lY|WsXsU|ZkVq$V1Eit^3*L)|WUdK-<Y;G~giGao6mxjnf1r15@1b%x(YoM= zdVXHR(|O6D?5$X$lch-6u2|=Hl4L=?@vYR~(<b0W%%=oB&!#n9ql89mzc<XK{VvW$ z6Ypjf3t?P}5+IdwPS897m9F{>p`(!r*C$oZfcdQt$LKzd3pPx(bs+%?w**dE`1<x! zXP(QaAc8m(+ODD1N}$>~u6-DG(0EpV-%k6>9?8^NZ)+H~Tj8!OD<mWvGc?B`{XPy2 zLV(oHQ$H!+NS0Ihui=8Kn)!XS18y^~)LYJ`>!X<P3DCxck&nJsR-?8{IswiMEXtkR z8JN$%yiFF1cb~PDcqw?Az^3#OE6#GSdJ{mM_`@o(6y?mYXDgjL1Dh7{wO$SOyQ?uw z0P^=5jCCXU?*?PiQKle*F8lnDKT+7IA9BWmh&=mOgH7wc+^E4;RJ?C!DAN}zsqCLZ znX=b9^BXvtSk@VQRJAl+3JBZI`#|rSM{A_QF)fowk|DiPT$`k6$C9Wm{V_?ZrHnTa z;pa_5@4i7qUyIKcr*s41{#g0I{jzy+@J@8nl)Yh9BJKm&0unj*5)r8+TF>;I&~~xu z*Z0huY8U@#uwz)Ob??)|pZn8Ua|Y3ehS%51;%^4aF0_*9F@}pV%Jh!?v~m_OHaL!U z%=5D=7~H6*L`DXf<(Fr-2#-*rDtU99C8b0!|8o1It#GPEif%-R1KE~p_hxw?9k<!8 zd6HAUU6UsI%HVra@Z-!5N?FH5HPquZXvJmqjV!BSPGPADXke>V6q1-ws^EG#&cmf$ z((^0d(c`dRS7)I?05iP{hzxRex=k^F62}}y@6!2>&xq$W)1R}pyk@sQXIE;nh46Y5 zZIe*=<0H}5ajdel2w^jXVeMHQ@C@35EQoiaz)|81hl|M@^=nB&LYqdG2hv*gwmgY{ zKpIpW-~veHz;lJ;EL0|(Fs!upA?KqB88+N{rFP0cXEpa;Vl8$geeo7P<nq7MmT<*7 zsy#Y{sd-+hl*Ht}h>U*l5Ix)hHTd;naIq|n2+Os@8u2j)lyl2_Z!ks)ng2#55FBvF znY--sW}~qWmI_^{IG^^H6xnKTU#SyK!|K$@Hh$g|L(LE+4WB;b0gGw;KkeOhOkG>U zE_&Pxh2rj7id%7q;_mM5?i6=-cPLWatyppQVx<&{dvVS_aBlAY_F;eD`^Qb1+{|B- zhSj8bMpx%})?9PVHAa;KeI=^Pra{l^)p2QczjV+T+c_!ietpbEkx_v&XJ}VreXq^v zBc1n{AJF~BsPt4>U%rxm*}B`WwekfF5*Ad<P3_|&t0d9$6)6erjXrXDDz)7$Ux>XT z95m*+sYQz<`IeAlTMfZ)MDV;lRRVJYdAC+BmNPg(zsz?FB?!U3NwUW+ycNzUVWg8? zYvl{AX?VFe^BQ8oR}Ky1esma6XZs{E*%#P7f5t4=sPswrS|m5V3@>L*ff_s*yx6BM zhsLfwHqENJt3?WXDAQNU$H0f7T%eNYSO}F9xpLeVcdJO9I?OC-mTuKPzYO;^F^SGo zgZ=5*7|ILvXB&*Z;MG4Htmim-a?Lxsr|O);z)u&P1jH16{O=8Ri_A&m+3)AD3jV!V zV<kAeirP~*$mwDA-n$LM`}EM4RVM22J!2*&yyaOf9|`B)GD3vkjsV?F{8|hY;&74W zK5kL1U@fhd!XZ^^94M0)WA`BK(bg$AfzCLwcerbwCHETYl~jRltUlr1H5BZmO@RFF zewGwc62B>dyJ+yqzc$!)*~rPSkeQUGbO+^+eB<|O(;yVj4c2zK#NVajIqc(U7N5;C zX!@YDns9SR_uGI=&S+Lw$jc+Zk^Ay+>e5|m#Y&3V$;8i-cCX13E<HjEj;$YQh{JSA zR#)9JUePH3K16=I@6C~XPl#MH7`vbc)k4SA35UTxGJ5&UJ1Iq2aFc4$Y?uk*K&1<t zNZL3yc1}smmUiQME1PBT@t40VSbxXg&C@7e<V0x@?a^Zb3C2tGw6v_)VIi~#)^Tl2 z5aH<}Ar9_xzBF&+>NqE5eRmw<y<*t~ruIB>Qh%;aUcA@>?Lu;D|9jEFK8X(cn9(#p zeu9%j(GIhciMo{b7gHIN=xn|<C}T9Fu4)`Jy*==}9e;ioNn5)`hoA#R5^4rEe~*lb z8~CnW54GZ(Gp?PU1F7=rolEtOz5{jg9pu9Z0w{F_9bGP-+L@ieKG)4&t{688Xfpze zN1e9U%b$Qk=gpl22P}TW*_5eVw>v7y;8*E-US-;`^n8q$qlT!Q40&r<DjdxPq^%<w zQlZiQJHyW`@7Y+cMsfw54@G$yr4U16rd~{L;9pv}Q<7>m;q&`hy#3S9oeYIU>!n^y z<%ftk)t?H^zK4#qL@@#_hEDks^OPFWLnZwFzO1u%zw|_-`+$^Qnu`4B=Ua5V2-6g| zcZ~WUs88<|k%MOz<cpIc9jV20!WMMSbS<w1-jbx1^9s{QAqB)k5%XZbnCiCBlid|P zn0v6B-9?o15h6JAlJxnSUou2rT+f`8LM`6_a@jyKQLo6Ma$jd)wYu)(5_jh3tv9Gm zLD2{z^1*}MdaI_8W`@3g{PWc437+U<16D3F=y2~z)s4EGo*L{=&&E(*$Uoa)v;rmn zY%m(tF(vv9$34+H6lYv#B@fd3>!rUp*gQ|sJHfL(ks{Hh;uEiG$egkz&~mQ^>$QUD zV)v-bcy$U?m3Ry5Z%c{a{f@)Rd)A5XzG+5kcCqZ-QRIjX&T|T2F(gDMcQCz~#duT< zQv7fz#J0Q6aPW#C$hwjrsZxoATI!Xn_rQ-#m(p7FHifTm8#cjG620*mdLt|#5@r6i z!8oqpV|9}r9)8lY&*mxiV(@O0uXt{-Zz0>;OrkK>WgndvvVRMrR5hSzit*J91=Ec; zJuWaKpl$1SJSe9x&sP%`U%4^(v!sP4ku2nSre7MI`Q(2f=y2~Y-k?i<$fav*LK|^l z?sovCoxi^M!75QL{0;(XY>Z?*QqZFwHjJV<WV8Fu*a(WGLT4wVcr*1kuVoSYw%P(_ zG)dCY(2Ga<dPXi;nL?Xz9o1DW`V~{5C)C9A9pk38O`20Zo2)*Lh$QT9h$={rA{Vl1 zmBIZ=)Ue;gHeBg79OOl<UfA<%_iUqa9s71wNT|^=Hf^q{-bQ`(7hZ}?IZPC|NhE&x zy(^pYpvn9q8lLed=W@eN3v7gwfTz?1)=N?W(d?PW6p|u}1d2^-Nilr69nyYrpG^l1 zx&!2(G3N^F#C@*`m+bjFmUrC=4<?7d-&Qko$PEsxo*)Ugl3u;h6l#3+V(LV9>Zn_2 zh*W;a4!QlW+Dt7&zUQ_s8T&0S8lAv+#lDd6DzMS6Q06Tpb~ij+I>gKaS&R%AUbAbu zyMFT8L{nJI<?uUh%EZoxUP#|Z>u9ntuCZ^uu=hx(_hDNvuhf+u7SXs7rUw1MO%t94 zO6KpJn)AG-Pg~Y6tFCAWYBDxN^xvygE95rwF}p{{JENt3TE52u_%OJE<&BGA)&kD; z+#Bd+0a|B|+K)X_zpqdBk>K)U3fS$<X{@7_%U(>q+We&Z9*%I-fK|zUgDX7PDprX5 zDOGXn;;2Z$@bgI@v3ThG_<CB`*OYFBGkcIU)~g75C^EdJ9d5e<1iR#cUd}1j=~v4~ z%|)Zp2JH}BND_o`pTSMgtV5>LjZ2;y>`%|eP+o{X+hCL*4gT3+{tqPRZc^C$xxcw5 z6g&h5KqR3`{>$n*q@}s|{D}@m+JjNTMc*Hy(~B>@ItA5nrIFwGScHD!OgiL3*NAx= ziW$S!Z3pTV2sfdyE>veO<doTCtLmWjIY=2YrTtE7TvJNUo3TO%IquVltKjdm=;IZs z**88%A)>~Ux0gIny@ud@&@3&82I{`5u9-Hf>g}2f6jop4{<XoP?p+GIPPre9Hx1;Z zvTWQ#+4$z38?4FHGoDZs@6*Ull3Qv%VGQb~kSLjO)(j|1e3*E;x0m&%S90#8Z99fy zD<kOc&jH9(>yt58n~Bl(juG_!QE=;%)@Tt043cw#wibbu``i<Xx^P6Tlq(Vjn_VdZ zUUR-aFrEVQLJ3&na16`{JAzbe!DX@Ut+!TFg?6uyvw5v?!UJIn242241bvzig5S7L z^4+*Mg^}3#YyNlJr>}F04Sk)?$((vX<UQ%4xbKG;W`(KGhZQBGf$;xX5or=7`a4x| z#r(IvuiSynm2!dq;T1wLuB`FaWe(lUZ&9<?L6mu87%C)CrZ4YR1g;M)qZ6gFwLJ4J zpeMJ^YbfpcZVJmq1EMlEDmskB+lV#UkvnsnVM|A{+sJ4tG!Agiqy*CYqGfG5NO%+# zl!@x)$P*VMS&Z}>JSJm#R-J>@`BlG)FIhYS@+B{(hM$Uug54|W@lU+ctUVr{Z)gu{ zc}fix+Ix@55t74MQX8yS?#66$dVJfuK_!nQ)JD^@5JjE(dXKLIlAEE`%2q?4N+d+w z?EWHfSl|Znt=`<(?#>KW(a>1)etz<cspaqwP$WqRHtZl@snzU*>d1#LU7qh((F>id z-T}pV<=ewVyA26LPt(?@ZroE^l~6jjW5}}J?F8j04r+_(Bx>5#$19?NuQX*W^blVi z>B3|9u+(pcCYL;2Ml474Vyge627xwMLDI^%UYKSdt*e_YFXiX|S2D=YYBNVg2NY>y z<GKmP29SH%4vM^ABIc@EI}~>okIi)B^Fdv0^rZR4GVgV350&a!Vf*KWcRB}`nQJmf z2}Hi{mu|j+dR|@U56{L>UhqHLV5D2V|7@^2;qu6Ln)fMOazgd^Uf=oZj=uQ+m%WOx z(=8#(adsgqbT`MGG^t|OXT}PZ?Qy0IgS962fBp6&ULjPhNntU+HQJ*rw7-5s+J=bQ zKoznIlIJcW(Z2iZ^Vdue3KM;J??sWachqG2qGT{x*y?oCi#YJ$1=}rp4^|zINDs~J z_ncmqE~$CT;H#iAITFLl@bS#nN%qkH+F-o3_=_XUYJGd$m&_q2u+SqJ7h%s0CW^y1 zC&)2gE0VIZ3ySEj-5)i(8omIZ8SuGGY2<L%1>GN8<5g~wHBD4AF26pSV6qYI3@v|9 zp*;##Eap2Tf&!s{oq#dFECww@>8h^+7+_aF$W}sFQ$yyPNg9$0iy-Ai8!I?1qNa`b zKfkvtmfeOQYaYYnP|z^!q$_u!viRk<Z-wM3^zs_WImg|NkH@1SRPeYg`M*Z9noP-_ z*N)evI#hvmz)4U%d!U~*yb)&Oyd=<jAcKIe+RSTPXCM(kBg<O*NJHd8U`nJ7UV6!z zy|N-Fp9wyb8(@DIEB?zT6+@Vb?*Qe+)Y3YuBd$4J^X-9eauq9`ir@kLHBYHV^)?C@ z+Qd3_Qt!UwTX8kG&MWGX)|s?f;cYwePQwn^$ic$$ag@~)I$c9WbPtYNoO1r&5_;<w z#lAo5tj6=Y$Y@Ck+MMI%-7lIR?o;&;^Ks4<zxNKqs%oU^W&L=*rjE5fu43^30v`<a zO^C?}{Y}_b?M%<C33EQRx!!!c!<(E$x@lN<EEQLBURgQbS8uq!5C^l&8ht8*Q$5#| zsgmRTHqh8w`sT&d@$%+u&ymF<<Q<;aqRsA~)Hx#0ckkv23}o4OzVBou@3GsPI81bD z3PGHR80p+(J1-y2`85DrIgPE762|&dN6IDU%`z+PM)cH{3}uwwu!s>m&94>_8;96K z$YJppQxh}t?ozsnj=dw|uqkel>2w&m=$}`_FtlKo=AOQK{pFVq-mDhH<h!CpDM<&F zFUovE1CtpIu|pT1kx^vUefkdq`v?;RjGfC63%}p2cHEIf9K?AP4UsRsHl-C0dETq& z56{L>Ua&vgV1$8t|7<Y7;pKoSbF?CUrFZiTCE9xNMvFp!Z?Mfld0reZA`P2~H(z31 zA4~gmi;il4fW|&%L-mrxeLz*v^_<({qEt&F4#PThK(az-LBh*~^vJj~+Dtk$ylXvn ze_Z6zFwQyoVBK)PylaWyO5Gr0qFQy}=Ylm;=Z0%^?2jY<^VDo%68=G!4iSB-ToxJ9 z{0@hhwlc!m)&JjjTWffSq{mR#nq~u&wDyxe*lUBo2>ZFg>ZF|2<^+)7LiP*@V%6~; zlCA0aX=J3STIDaOJ`BIDV~SsvI$&3Xwcn}6t1i(&yZo;6ph)!=(ce>|@cc*zwVj?E zhANZJr9&&6(oAJ`z9TqFcqPx|=lXbx)g@(RX)7D(r`PX)!S%Hiteq0$4t?*5(R%G> zZGB`s?XdwTmFN*;13NlukpA*2?&ZmlLB8c{VUzu7IK^KvO-*vapVw%0|Csoe9S)Yi z2LFzN(XGl@RE(=g;rvQ^r$}cfI==cX3+B`o=)Rj-1kQu7lSCc)l$l(y@`sHD6qpX! z4EM}+Et;{*jl0h;rcy%X((9P(&`<PHyGRRTO%7{ewmqdnOPDr*<ON_2C1L1VQ|!DV z5F!!Hiw~(I2Ga+*Dphz3GcmN^KetbqVSMe+PS!X3ku9>k6oRP#6EmyDV5-YK@?tr= zaL4G&dnl@$FL`WH{q$aHLgP??+0GheCwks<<Bq4Xw&h*?iDh;`L5JxC*ss@EiC>xz zY55eREObXSv4aZDV!t1JT7i_Fm(e?05xjQw))&{dgE-Oe#`ZVE`CK;<=afo-_3|Be z-1><nN!g?CA_;oi*<8rPwB3gGo>H|e5mU`_HqYNk_-wJs*e<(<;Fge&<3KfKEZ&cd zT#LRsyMXuJhRA%h5rwt&S16imj&N)0R<rVM8*wjYY4;E3ch=G#ckOujo82t2kyl)d zr<#6sIU3B;XTi`F&sX09+k?WIE^J47)U)OiNklk?ZLg771DzL4aT@k>xTA*2+dTPH zcSAgn3Hd`dj?<T;#>cLHi!K~GXxI&^g5VA6C7Uy_a=&SMYOp^&8$)?P|7?Td;{0EG z6-ilA-F6ZU;}jbbQKq|ST=fYD)&9M~%HmGiZsPlB&))tpZE3p<#_$AVElIU`rEq`_ zL4ICMX%#sI7H#R4&8X29V2R;G9?Tw(*eQy#LD0b?X`~!Rj}R-_?<R>%|4YTr8(qtJ zIPfG*)D+(Bniw;%c#eFuI;hOf^&Y<VEH#UN)RHE1yd}1T>`Yu5N!Egm{Q?W@-#?2) zkhbxZ=APMfOdVv)c1~GCynXY3u7iO^r*ApAtF)XEAf`b-5bv_Gj~9I^FEGR3m9eE& z<ge76SA4KLV>J0-eaAMwSp$DxRy2|^Tip}ZFoYDVWh=%E-yJ5c;AGG%aJdC@)|;U8 z$b1oT;7Z-2DhKa2A=wW4>ysT=<G?RjSq(f^vOyVQq1g!OFCqPwiU}y#=%Ty)c-C<8 zziU-rUR~$I%%=#SM-orSqmDWuUAGThgU{ceqgm^EheXj3H)imCLCYF?t<zqH3$+|X zEV#+*wi~N@CHRigIrbLx1{LalQCX}U4X!fE1Yu=AyRl?P0*3q%geTZ~tr*Yp<sEP9 zCKMm=n?1r^`~=7s`;t8j+XCpGQv1(Q!LMp@Z$$B2OVA>}|7tAWMJO^B(~pSS_gZIS zJuansMAiz3V!j!*;DJO@rxr;aF9$&mNXiEHx<nR?bFh}fW<AhxczM0&(}p-#$wP8U zMEwu92rB3AeSL|=p6^rlFH->Prm2$Xd)+Xso_LNA$#y4Ry#tGv1+6f{AFz}^65`k2 zfnr-OsuElloB>m#FZuTMn#mOPC%=)zYd&JBS!*ODMWn5lSJ$bhM2r+~Un)syQjTmf zcgt?Y#CS@D>^*W+Q?a6|vlGa(nnFBwLAVMD?X>r4W-YV0ErRapmnCaspkY_Re3$Zd z3cePN$ih}R-hbnQg%s3G3c>&OlU|MUjm6~4tC9&<1-HPo$J<W@^e3~EdtTGD;XSW{ z>#yX`3HAv&gAbRylpE)6ql7O)r-28SuJ`t*WYvxp_{ajQAN@yW%f(`cW^Sp5^dy)b z<8&f?B<Zt(ASlj}%DG-U+y>{4>8A$!)3fn^e-H>Kn*`#)?d)#XXhmsiWRR!5rjDzm z_SYW*000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000`2Vp`K2mRpKe?_d^5wD-$UhcV-74uu&JQQ>BA5>3 zQ(|6!xV)CW|NFwK1G4L8Dhk-95W^7{Jt@=58O(4I!7Pk@v&HF;o#VV-c3%6FL_2$M z$Qz_0Ul(gYU8(P*^S{Zy;%~a4?EcB6XG?h|Iz{W*Vt1{$%Baa-bCjj)e=n~JgFh^v z?p)k2>Yt5lZK!9D%ja{pu5YkXv1otc=Jq3?8Rhi*dAY9TtAAZswFHdmJ#C9f^Wyzg z$!;ahFKy@8#^;4q_g&iukH(Fcp}V&~wGDtQ?759+5#I>Jo7{}XWA4YBD(&xcA78fm zU^%^iy@UPn>~RTQKH`uFA1c9E>U4T&k-wWGo{)&ln$618+u7ak8|*uHRhs$(=?jUs zk9SP(hJL@jsMQA7a6J7qEc&{Y*WssLVC!)IyJ0vcAzHfPgA+;zSD8)Gy_dJCb;)S_ zj1S%tJ5hBJd-%FsLDw+z@@WB1>ayTff#MbHMS&Uk18ZtAH1BWuq~=J`W?x+*Q_Tz^ zb=Opat>Tz6<C3QwE6%>?m7d<D_fRcT@xmOKQZs3vBQ2;@P5w-5dU=s%y)W|KRll~@ zD4h&PomD+cb(<QV7aJAKI=<0QLZ~b`_PK@1d*D%^`h31#>k4|)eIZ1f+@c#%>bv|F zQjj8TQ6#U;*u{2kSbo~=ym~Q_b5~<Clb!v`C}AgO!^@jb<Iv6A1aNk@f0@03I6{l0 zA#h<td`h)9pnrX9G)L!zPkNhye-8UfQzzZb2``jme52|v&Ei2&qzG{;`St^e_0HtV zE<cqF4sFRTm=}vMp3ru9YxI6Im(pj!JGHDAQ$HuH@nJ}}Uz#Hmgujx+7v(3oe}0DP zLKMWE*1ru>b{6cY_g{_DRoS7~q06y<=6c*q^f=z7T4y%b7B$CY#R1L2v(Le2a6j*R z6?av35`Kt|lHYTe8CaG2`*VZji>W~me68<_OyAbBk$Z~5kaZZwBzZrj&cFvg7AFJ? z9)^HjlPYH42W5z{YOwMk>dNEA!t<fFvSZKEqkTOru~o_J->`Hnc4cI=dC0s>%<^@a zoaT>-djDBbX*%8D$J4^9e|k2C@{#=E3R_-g{AY!A814~ztiEM#HT-TFwEDx*WP{M= z?-kaNJJSNkI%CDr@4MA;G)h`;!}ZEn+(DPH1HFRRr%d|ZyKAa5^lenXAB_$4EUDQ( z70vrKWo|U=M$_+-A4)$*rXe@iy}_(3Gsbk4pfh#HRN-4nqV=6uX8+}}pQfdP#+-Nc z!xUsK-ciwj#(aiwWeH6CM__Nwd-C@O)b&Mz|5{;C-9p#bqO5)?GSX~Vx77H1y!&m> z6(%yGZ1>rrpU1`&_2WIGi93sUCuip&`3@*Ie-1tT&k2R<q@6nN2}8s$ZF@$%jtU=> z6lX53{qo@`)fVm4`<*3ryJJ%q8>cnXVJIFjyBGAi&hDfLT!O&3(I*;N%neeiKT68} zjP~M%?h(XP`;0Y<9i`**DYJa3b@q`H0jnX2)yJTyUQO`DL)~V7>!N6*%YC_^H?`>( zs%fT>=KH5ZJ#6`Im{+A}U7+z3o41Sz9x=o?!F!9Aq8~a%+kQX#zG9oX=bL*ff8WRW z`~h`TaO+N?B@xSE{~XaVQ7QHmYWw3y?!~W$Nnc+~jmPGt7FuHANeMjUux^eh{p?sR z@RV8{k*$I!!5;C`&8B#Ml!`}$hReHdp9s-i-ZkiUWa1D>r}JD8a;oANR;b_=7T7=q za&IOyo()YmCCr!m!v`O+yUafPLJ!cFceLbJWYt%1kgMDXK%Uxx+*G#CdfrW`+fged zR!`+nkIAN_XuAeX4vBgQZ_ry@=;t05>^MutjbwmLeYud3GMePB@kx3@4~f<{SLb_u z=|CN`;gQEQCQ3Xh*YttkFQ!JJ<_I7N>LZR9yJLR;Fx(Swg;x8NdLki!WXs~0gyC|o zSXhxF|Bm=diA^_MTa`}56+7eAWKhj@x7os=qa!qnI=;W*{O5!l4~XuoSWwT)Kza>F z710R0Nxjab^%ql%<*ZXWef%(ur??iQ6FVPo*JqyhNK3+WzJy=JKrf^=Zk!$W(o!_g z2O*}sWbJHB#rWYnu1+MCLedh~HZQq)ujhF5LxJfaZn*p*IY_<u=yw4scXmH3z6Ld3 z6WCLQ{pr~l%18WXD-3C766v26#!teg+{o*b2PKjDQC`rZy{b~_>hBdcW?05S#Y_}( zmcQ%x*K3AEmLMGRfYBsUqggvkK_&;o0nZ}Bmd)a9_sTxk3dQy)_LcQqaZSJcS(}xN zShhOYsn}coa?~jB@`*KkFxgz3%2a7$pDX;vaqKU!4LnMWyFB(}3LkF_^u0(w<=g&< zPHi9V;%h=qwPwhsl%z1I`qv5@J*%v}kmoSKi?}ksI)F?NUryL~uCTr2$tftTgCDkr z@-^SmZZgXJWW9zm?Tk&q(wlo`KYqu_mhUe<Yj+G>8#p)H;I~*tzjs$7mE1e{a>~t9 zE*4Md`mQb}HIS1-9PJWyK6p`dll_}FMSIwJ2MOg)XKCVx^^ZRjeTp``T|Hl4)VnKW z+x}8i>2O!Iz;L^uLfNJio3FIzes7!f;-PMInHBe=UdjFycM36ao)P0jUijeCp^ly> zMT$r|8bt3*3LjFbzW}#ah3amu6D^E*U-mF&U0v$;%eaA)20hn4&BG)cQLjyD0B&qD zcBwZ;uNP#^?#C5_2z)$d(8Y_Xp4(TDJmj_#mthPL`Vt!<SFaz4o>Eug;6j76oF;5t z{d-1J(MT}373Ln4iYk<dk-$*pl)(v*l&$U7X8b0@3OOo4aFR<UZanJGu3+shh*kx& z$k+21b+lrea+qFB^(n{6Hgj-O2%Ja}ASkx3m)P1(eM%KNV`b8G%1z(AbZnwvxC|jm zY&#hs*FLZEyN=sv;+)JbgR9YGMDM3M74bndC}UlV)i!j3i)nCiAkE%-Oojfw>~G)s zl~(x0RMd*?mU@TJ(whDO$r!D*ycAZT&&%5*{PwR}k-E9`6$e$)|1ofA0?h{Rf$pJ1 zgk7Rsk*W~U?GYgt+Zm6X93E#w&E*L~=1Ar*({M@Kt(-3-6|yNcSjy@6iNxsTZRixw zP?4(*kFCLAeyJ}IuM5@AS6V)$zA@sLz~d`EuWDDEP4F$u8baJ$pMJf6<gHHV*L~gn zuG~#{&p*R<1qUL#`w$fco|3V%JKh3aytb3EJjHs%m0WgWNE{(k|Ea?M^lS{}Bl^P? zHaKSX&k74+(AQ<!KqNZs=_e-7;qaXn!Os7Cg?;hhqf+<ON6OFSXSJ8$or@KD#om-5 z29;{R?`sSRrd`eZVSl*tpiIQcgXT=mj4gQCNUtNT*cqb~+JO;$AjX(4(>eODpX=k) zGej;7)OZS(x<DdOa_1{?ed_~yU)NzuEyDF~+I*%U+boDJ<8!D|8?vcW<d%4yhF6K9 zS^cjS=5r@fz+OY*({UtB+uyR*6h~4@@?2pfuByakv?ZSgNz#ON?#?Ug6<e!E8b!IQ z6X>d4y(H!a8N}6B>Pgo1<zTQ=(&j2ckr_fKK-y5d=JMb}WL~-9^D{<9t__Opx#27d zuy^6!FxSJ1=VjWW#;SDs-3_t%K3*A+EC&qv_dv5NrQ)vOj<NaDK{2}@^6cB7s%eHP zT{;oNtgFDkc&I0gU3!4{@<1~$L|fKST6Uj|YVbZC>P5+dOCL*c9u-~}RVyS#i7V3G z@iP>y|9)Sn?;*-l<WOU^k%uvK4|`*YwIJ`+GL&;m5dD!}Y9q8AJ>rye)Iz;eZ-fAG z;^n>UYh1Yt*Aiyei+l5nEOx_NuZQx#Jf$8B$E=NK`Q}gD*!0Ocr@A*-iOJ4{3DV7S z6+|$Y`W2mm(1M5mFjt(2x4P=NL55P<o^lQ12t#t;s4Mwd6k8udhgHmcP{8$KD%96r zYjU1mnlc|P>Ti7~-Zkv)7SB5_PWb<r7XwoT5#F~(zr@Lhq7ZW6%44PN>wrPNB9f42 zmP>FDkdcU;il`-8TO(QUWVCQEcd_0``#@oInkUHJ5iyC;h++Bq#Z<ZylbUNsKK(>D zDNoX#Dow>&+22p8<Q6hDA<EepsnR2OHc29ihoP=1M4#^3cP3RXU?XM`S%nM{-h71{ zI_;Z|#Cbe8HS^QC(a2g7u*_-ND1*MP!ymlF;ook0d3VVp@WyTv%%Ltq3$ynm6D|qM z;R+p3sp;bLN1OsvEDvTmiZ<TI6CtV@NF8Lg-$e^u3l8S0A7vqM;sbDZgMZ|cpLTy# zOh&?8f46mG+J{a;Z)hr(FQBw5bxnwWhw@Zme|k2C@)7>w3hTnP`)7rn_>Rhza3zmM zHMSu)&@=Q}=49^wy~1`({6pUUCTp#1CwtwFS7zR>zf5{1vhfN9EMBZ3N+T2#WqH0J z-S>Tjlb-1yFS)9nmvIp8n2HW{lzf~xg0`2i)+^SZ*SpS*!%>3iTG6lql0>eP1|hs% z{-5A%AU?jE&2yiJ0Y{2oLyzKMKpXU@Ricd#w2|URnKzUhKBXf2_p`8-bVOb=l%0Ki zNq9N#V4S^p6P4!*)5)plvUkSSMe3`|0atZl6|BsJ{$_W1z`bpZMHBLfP|-f@vDH*u zSgJ%pw`ErgQ&>|Lcibj$5Lx4lSC35*JHbco7r+5WW+b^xIzRc|sYR|iHpcaIJ;im- z0_z?FCl5+(LnUognpF0X#id^|Lv9OSfNZi063+M?|5$B6fy%mCs?p^`@r#GLHsz`N zR94f9Wj~6drp-|~qoxk-)1iLbMFDSzY5t-4A@@E1<N(HF9kx2rUauR4xZB+EOq~Yo zYp&Lx!<sv_DqkCety*ub(2DC>STilLvqhlNj}_Uy`4vB?PB(pgF_p>{Tz$?hNG`^= z=fsEkd!Xv5q~}wr(qoSU10e~IH-5x&F0unZjsW~vEs}9k=>xHqK<=CPfQJkR3VVTE z@@-8#f^sXy%u}M4IF<`99cYE0DK|_!e9Nw=L$dZKFQ&fVowoN|;aEVR5B0*71Ftfu zMgIPjS__?arc)(IH0OF_dL`H!44I&Jux)?4f(ha>mxWDmWL(8k2(kEPvs}=X(1o#^ z!}hdy#BrN(sv|$QK>4+sBPPe!HDklty%$sC3L(0{R~nnciVDmkA8w`<eqItkrHZ9@ zezm$2IdUK7>c|`o&uFEd*@Ry>sL4k<$Q;sFt9;Gi?ozxx3#ZFq%Js!xj3!L5Qlj6L z=_V`jTU}DbKp>~3vx&J|wdjkfRaC|*bTpna4So^%iEy%)^`LEv&u7=$@v1WWgH*!X zWy2v4)0bTj6Iiq2DQzAs<;gVr;4bq-L4Iz^$67v7RgqDTbM&Cojw`jf)@lUzeqDyi zM-vxSu8}r)QaMi*_NQlKC?CNeuCTl+lYds&>xk{J(j)z1r&Mf?NpTRDE@T1azgO5e zTEnOchI@*XE7QBH;aClL%NY~%*Ko2mUo$wvP52fP$9}x>cD%^?DeI8OnWu|$pK?y| zjnsoRgK(Z>KO7lNQL>@S(N8gB$KnU;6s9}8Rm_;u=SsV5$ts(wRsYKHfKS#Gy?DEJ zc-}3tm_srHfyD~2gnbic+mH|gGh>kdeip{j1NWAOLhN=fW71%W!ZjNyx6=H%!t~D4 z#qjbN>Y3*~xFgF#R5zod_F`C4xIcci=)S!Mx4pHBu-1_GVN<O&JUqEtR_LZ;Je}L( z%kT<~?VZ_Cko79S3L|*EfT^M%$5I{$S%W?l9*ry5Scfc;*8!nqIl29-kb~~BKwGU3 zKeJoihy?4EUWzX=5y<&^>j0|r?+dBMpSf;uZ7=Wi*ua&@#yt&bsj#`6K^1zev5=of z^K__V!*!CrVq3DC<aD0c6{WX{o*75@s%-GV*@AbH>wo~7|92ZJzn&6w{}p8JH#P*` zuVNN%_hkrvct4lB5d-9s2wLLk;{;_eUf#_<33_DW(q{Q95l6GAH;+`T&Sm{m>U*?h ze)9K}ox)psL+)NRLMC{IL&n(Q+JV+d;a^wR{ls0z+paau!KM+<FW*BcRv9-X9#HuF zGQaPjNT%0&S85zCKi}0Ur0`;@s$zH>Q^d7xcH{T!uN?CiD4B83XPgfK^)<c+Y8tEk z!wv5UFp4rZ`TR+ou1Y@&UVgL>5$_Q<9jWa0kByx$l*CRya3i7aN&4-F|A|A3w<+R= zHjg|Em$+aSuUz8A)ZpHL$*8>ODz>Ki9McU&J%33Kyr<MDS?4%C1)V(OPN#Ktmj=Q! z{KEA|6eH@SVivF==)y<}{*JZZD(|?jJX`P*hCrj85QE%55c)W)&z_MSeNZaxWm`Ez zPsZeVd4EX?t55<&0qdTz1z0f_9`oM_ea~l{co=&F;~mWysUnz8X11R`E<$reqg+Gz zMY0Yx|0H1WW;=-1xedyRYe{BVl%7A8Y2%Hn(GYrIQW`(e7f`8GsbX5E9i}$tc&e~J zJsU$2@c*+4gZZn%Ed5OXpI`sqR2a-(zs0B&{j<W*t5<R)l)BSqr3Mij>f$q8@dOC| zUSWe@<I(xly7dT0I4GA+?X6XB7t)9X$DqoXp@$0>MMT4R4Yc7z6vn0I4lxrehDze~ z^l;-u6XYsFZGK?d46vV!IX#edugwU07WI6zETcy?VdDAyx~TugM3<nD*nFt721VxR zjEV+@obkY2Mz9hoTkr$n52^ClK8HaqhNl9ncxf+sIq1*k+XzRh*n6s0*wVpV4xcoh z3e5NBehhne%v!Tc4U7eLL;NEF#9>7FoJ&P(@qzvX_tzQP0tTew>m`0%MffreM5h~~ zR<Oz1&w7`pTf!FL(F%oc5a<oJV$&Bl2v}Ewpe+~uGnRh3CVK?psVnuZy40c(KjsBR zU50}oSHY&8?8;^s37yma4#z*g9I)LpNo*dVw|rr^`lx8-HTkdTXW4D5A$KE}goEs= z{`YYC5OJUl*MHa{G#!xk-X_1H8F%Dpj?DTvY>RXv=g-rYZ9IHU4<)+vMHLSBkmtt< z3hRy<rOXBSs}%Uo(y5qb^cT|z_@vhDS-)0nsR^@$t(2Z1OMPE@N^6BSDn;DuBwIrN zu8B{o|3zy~bSOTm=5jcaXL8kxs8+PhUC!AL=cd0;j4+qymU^unI-MKG78T$BK16Vj z^76&B53z-tu^IO^>(HmVzeg$`-*VS-J*62iv*QvjeU?e&aP!X#QA_^x$T9tDo1wC2 zO&Q}%l^@PpHn1;kV-GqFOyL5Q`2dHJR&Z|aTLQc^#rx1&7kv5`(<*d|$dTbGE_32- zLhmV)%wltI+Mm+I?kahFvV-wzk8IAfzu3Gj)K^&e{$LU402c5TQ<t%Xp&?q)!p3$t zXcS~`3wyb^;GM4#dTwc3JY3BMdWSygi)nF>(-}R(%pYLPzaSjQ@8uHr$QwSTy(M@^ z#;L|$zwk6U`n=dBt^U1A8C#lGb>`^(8a)(r#bMVcu{^U0i<uk@vKcNqsM|Aqt75Rt zVex`KtmH|W{ij0uvvVty5AV;OQGrBoj{UQaP>(|Kv%8UU1A<yf%gj082fW5||6WIn zzGS3nzv<4<MG&$!3j?bXlnlj9O2K1us6SA*b}NXL=@2f|9P_6aSjtab2A;M?x*K{T z3C&FpL8lK8A>fDVeV2x!eqVuy?F$iiA=Yua8+eR$6SmGr+&3!%&1?}i-=UiSoo#?S zqS_!13^VQI%@}ft6yyfo7n;>qI+}hY|9VD+@%R(MI)d~rVb$vF3)l8yL_1&L^BL8^ zl|^e$cV360lc%-hr>+(UYztG3FQ*XfboG|#;WcVPU5RDa2SHK_;v8hU=i@%Iq0LQM zzg?oRJyR${+WjBCn@p!Bk7hdtDt>!Y&w{4ZU|?V&{)TE*4Yu<xUm__IwNS@JJd+Dn z_yWxTfttifk|`}MKpF;O{ii<h)}pA}o7FVV*^CS27hieXr(pP~u9^i7qGgfZ2Yt!6 zYcA1ydgV=f-<E;@9v@RZMKQ%K7rXJ!H(EKTYKNr>w^ALy8yC)mozgnv7lGD^XN*}h zgm0@=LfO)n0=bo0vm3`H(34ctQOpTMtnT?2Qx84czJSyqC(V6kU*Z%+(bK8vR)5~_ z)}gimlI!pT-+nh!P*-JuJnzltvBI)ER<mOvsOn%@zi(kJioG(U2agUL74<|DkdWjT z#7o;9v`ZL6K_J=YE~(~XJ9V=!|685GH-!_|9lmY&lGN($&uHczaZ){{>dy8KaX1_H z@`b7AG$>oit9kiCdj%1F@;4`~n2?0+S<uq&oa3Waz5YBlU1zCL^0k_iN)pq<yN5{E zyUlC7dsQJW0Tx~8<(olRn7v23*){A<-n_>^ub&y~^gKd&N?n?Xn5SL5aEbWE+$mIR z8?C{%=7S=^wbya1C>h6et6G|R_xV#YlDjKqy=>r#gg<$)6@T9(u_wV;01|bSk%6EO z<otoCao>xnqrqF5UcxziojpSQnKQO|nH=MwPpOETKipH{;#N^<RG|C2h@C~qU<=Pb z#thkkdB4W@Xb<IPH~ZOP+@!4D_2?!M9U}Iv_fW5R<p;927>r;;*<iEyv`wJBTd3$$ zf&J;(7|MtHhbt`P@XbFfEOtHrFrM$4;P5LcL}ue+*bHm5<bQcnxWcan#?E^lQSh_< zSl=b*rVew@6mOC)xKy~+`ge)8kmZKPRhWF=VH$TTS`bLR3RAWzUdOi*0pB|W7!Ca3 zJJ2_9DAoweOdg>!CMsGd6;;sY^K5~^ACY2d8Hl(YrQf(-9Q{H(G#bJthTZiKP}Pvv zj-0S|W^vT9T12HN{MQPzv$L*6a!t_B9BFoI!suQ)EE)XxTw%IfXD!Jdr6q4Et5;46 ze{i{vhIM#yh)Pf&ExDS{4AOgoa>^KNQ<TppkFq&qKJp^K#($EeC5wq~%D@BxvEI&- z{=9P<3x5-9c9>Tm?5;+D=GV;X4~I%?Z|_g3X4aRJ#z*yTVLz8qSDQEMoS=Wpiur_& zj`+isDyBda!=eWp>&KXrasz`G5A|J#bI6nl8HXqruC&X~64Y^t@YYX<Iu8Z$mb4|$ zPfB7-f>9MChGscB0%Mn+<1rydrUo+5!6tt+S?%{nEoA9Gamy`#DYNERxYI-J)jhZx zGax8yx6>i7qD(PA1ipOVQ_XK^;k*&O#<ZM3W*6*1ORK5Xr&O%BQEf0$H2SU&_p{7Y z-XGTz*981pOjMx=2SeyzyY4cdeH+aDHl&wHq!g3Z7YXOQ|6#0emvW_^=Ei4D1v`;K ziPTh09O~t(X?f$^UxaOUeP!j|;lT}{BwrVeZ9S!CcEM^$DtRQIOKXrYNC*FV$NEmp zv0~yvMZq-%H#zQ8x%|rejF5g}NUYIKm|mw6F>*4Q6;7>bhq!l_4Wfv;Yt9t@*llm* zUQAtU)~`dcaf<pD;QI9(3Pd&=Li5A(Z5hAT3s6WYC||2+lkzP*=!J}GPPVGMW~G5A ztmN;j29V7OKq?@d=(Xpix{L10qE>iOIm)w=-c@eF?Mabc&4p;BTZr5FEx(wWB3&C^ z0v-k>uP~Aww6@J}Y*3Z=l=@|Dzw0}3Dd^$m?2%v82qIgR4!MQ2fAg&mqL7=n;9FKf zd93SclU%<7mH8P?aUVL_a1o3_M%wqnWSR+b`H0MOc#aYo(~i$?3jgqI4CTZ5!xa|b zZ~V^+BmD(2@?&IG;3uP4s-_A)`6k-!f2pu<gA4u9X<XtpNPa}-F?tdso+o+iw<yW= z_>otZmmtED+6$aAefCMXvAP8#@Sa|f;AzYsTC(eP%JB?!JCb?$viLaIvey;vY00As zQ5P$TmN2&2d|#=D@x7+AMy5rB+f@)&t#CQ9xC8U@fJj(lc!f2E*V>K3__pSQg_F_7 zzg8Fv<m5LkQGH28+Lnmh1tMY7#5%m^3cI^Y(Wp;4@T;AH&s<~g7P^q|&`54J=!U#P zebYIv9nqRPKG<Zp>YW7Md#Lh>I85c8zH;v`F7FbjP>Jn&?Si@e$l3c?Zy&B<`$}~s z16%eR_}_TPpLVd^0zv%?(sDM<2yDle>bJ|z7EKW-=0jIy@sbWjkixt9Q*M_WBnn%s zJJ<Hdvyops)F)aFCrdO87hJ~){EjdouwXJ#rk_7uY<Q)?+X^lr%rUB)ySEMkr7+>F z7>2@KF03`}Y;!Oc>22=a`>1`GU+$g-r#V!rlh@rI1yj&Y`RkG!*Xs6)`vlV;8YgY! z#njEX+hl_68L^ATy>1358%-TZ;pcM%{W3*ugEkE;Jf6#;a{X*FVf*zM7gd|f!ruy) z3f-i7PMo8eTVIe2HOR2uo<?@eYst=OY<1j(hZDBXz1=b7jAx{-f`kkSe=!yA3lk^q zA;{YqjS!VHQ#b!6@|(k_)D?%F^?{DI^{m&q0~L2_>!V|yp~yDT=Y~xfqPIb@l-bh@ zO6uvq@)p>;$v;ivjeJnG=H4UrxkD4s(?|^_z{PXgk_V3&0edkub#*~?(jcBWn{U-H z=|iDR@lZMAQ);$sl_%&iRsd_`>u>3JR^|5Xm$eY$OewSSdjTgg#FX0|l@la<d_iZv zZLewtPWyS#7VrC##ID{(nfyG~Kxc~Lnvq1mI$3%#b#-zv$m#Qd8S0NfI<iGMRZ_o- zxu?_^opgheT-X3%ZgvhYL#nu6!VNs{W-Oeqd)T;NVPU2sm3wl5hJ28|bAQ8FA9$%n zzvy#;j2etfwCE&XEphN`e<F0K6RkSrslxvBY<%@sX+U}6g7~O=*Lfp)nR<Tt>j3}& z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0QkT75r2FI$?uN>;$UW?V_;<OsKdZu%xb95%Kq0Q7A96E77ltpUwTa_*#EP2IwT^! z3_bt<`w)Si8G<l;2vMF-eDr)>`skH_%~u$nh10xr^4$HqYyAQ0D=JekP*5;XLV7qT znCHI+2K)D)Aqe9)&Kz^T^ng2l#$H?d;>l(l;GEbFrYY`L*2RGC)1vX$C=veqQL_IB zqa@XHDeF&Pwh_3>_+ShoV#&`UUHg^q?ofAM^J5#M(f9vplsFw*Z~X^;STej3tREF} zTM@yj?TdJWu6xMLUg71oa{SjQ;s4uF>a+X@qf|~V$o{PlM_8xWY#-iyg~XFCYTC=^ z^Gnt_L~yL$Q~ZA#WyEI$r~A`(rJE@vGF>k}t!;aY>mKEdnPU=kOjX^7sJ})D_uoI4 z`v1Ww6~|&SI)sEGiBR`mo9}@~t(s;XIhN&nhWX=S4=;2b|4*Y7@^dr1Xu0Y>sBDFu zRZv|IE@du*O;oif(n-!|jt;Q+Ym~76{d39uAB-{&#HFFyuA$^m#vhb6`6G3o60e-^ z^mdz|nQLqBD#p(LG|HIY=2KD>NbheGWD@hUXj+k&Gbm-mKPN(<A|QhWz(V~sN|^ut zxzzg)MrkBWBw3Jf8n`agtob2h2{GT@dKrz%bauXR!TqO)3d;X9%B0=V$*mxTE;PMO anFww|n5mydpX5?!NVnLX^)^vrwEq_xwb-Bl
--- a/security/pkix/include/pkix/bind.h +++ b/security/pkix/include/pkix/bind.h @@ -57,16 +57,17 @@ using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3; #else extern class Placeholder1 { } _1; extern class Placeholder2 { } _2; extern class Placeholder3 { } _3; +extern class Placeholder4 { } _4; template <typename V> V& ref(V& v) { return v; } template <typename V> const V& cref(const V& v) { return v; } namespace internal { template <typename R, typename P1, typename B1> class Bind1 @@ -108,27 +109,28 @@ private: const F f; B1& b1; B2& b2; B3& b3; B4& b4; void operator=(const Bind4&) /*= delete*/; }; -template <typename R, typename C1, typename P1, typename P2, typename P3> -class BindToMemberFunction3 +template <typename R, typename C1, typename P1, typename P2, typename P3, + typename P4> +class BindToMemberFunction4 { public: - typedef R (C1::*F)(P1&, P2&, P3&); - BindToMemberFunction3(F f, C1* that) : f(f), that(that) { } - R operator()(P1& p1, P2& p2, P3& p3) const { return (that->*f)(p1, p2, p3); } + typedef R (C1::*F)(P1&, P2&, P3, P4&); + BindToMemberFunction4(F f, C1* that) : f(f), that(that) { } + R operator()(P1& p1, P2& p2, P3 p3, P4& p4) const { return (that->*f)(p1, p2, p3, p4); } private: const F f; C1* const that; - void operator=(const BindToMemberFunction3&) /*= delete*/; + void operator=(const BindToMemberFunction4&) /*= delete*/; }; } // namespace internal template <typename R, typename P1, typename B1> inline internal::Bind1<R, P1, B1> bind(R (*f)(P1&, B1&), Placeholder1&, B1& b1) { @@ -146,21 +148,22 @@ template <typename R, typename P1, typen typename B4> inline internal::Bind4<R, P1, const B1, const B2, B3, B4> bind(R (*f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2, B3& b3, B4& b4) { return internal::Bind4<R, P1, const B1, const B2, B3, B4>(f, b1, b2, b3, b4); } -template <typename R, typename C1, typename P1, typename P2, typename P3> -inline internal::BindToMemberFunction3<R, C1, P1, P2, P3> -bind(R (C1::*f)(P1&, P2&, P3&), C1* that, Placeholder1&, Placeholder2&, - Placeholder3&) +template <typename R, typename C1, typename P1, typename P2, typename P3, + typename P4> +inline internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4> +bind(R (C1::*f)(P1&, P2&, P3, P4&), C1* that, Placeholder1&, Placeholder2&, + Placeholder3, Placeholder4&) { - return internal::BindToMemberFunction3<R, C1, P1, P2, P3>(f, that); + return internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4>(f, that); } #endif } } // namespace mozilla::pkix #endif // mozilla_pkix__bind_h
--- a/security/pkix/lib/pkixbind.cpp +++ b/security/pkix/lib/pkixbind.cpp @@ -26,12 +26,13 @@ #ifndef MOZILLA_PKIX_USE_REAL_FUNCTIONAL namespace mozilla { namespace pkix { Placeholder1 _1; Placeholder2 _2; Placeholder3 _3; +Placeholder4 _4; } } // namespace mozilla::pkix #endif
--- a/security/pkix/lib/pkixcert.cpp +++ b/security/pkix/lib/pkixcert.cpp @@ -147,30 +147,57 @@ BackCert::Init() // Extensions were added in v3, so only accept extensions in v3 certificates. // v4 certificates are not defined but there are some certificates issued // with v4 that expect v3 decoding. For compatibility reasons we handle them // as v3 certificates. if (version == der::Version::v3 || version == der::Version::v4) { rv = der::OptionalExtensions(tbsCertificate, CSC | 3, bind(&BackCert::RememberExtension, this, _1, - _2, _3)); + _2, _3, _4)); if (rv != Success) { return rv; } + // The Netscape Certificate Type extension is an obsolete + // Netscape-proprietary mechanism that we ignore in favor of the standard + // extensions. However, some CAs have issued certificates with the Netscape + // Cert Type extension marked critical. Thus, for compatibility reasons, we + // "understand" this extension by ignoring it when it is not critical, and + // by ensuring that the equivalent standardized extensions are present when + // it is marked critical, based on the assumption that the information in + // the Netscape Cert Type extension is consistent with the information in + // the standard extensions. + // + // Here is a mapping between the Netscape Cert Type extension and the + // standard extensions: + // + // Netscape Cert Type | BasicConstraints.cA | Extended Key Usage + // --------------------+-----------------------+---------------------- + // SSL Server | false | id_kp_serverAuth + // SSL Client | false | id_kp_clientAuth + // S/MIME Client | false | id_kp_emailProtection + // Object Signing | false | id_kp_codeSigning + // SSL Server CA | true | id_pk_serverAuth + // SSL Client CA | true | id_kp_clientAuth + // S/MIME CA | true | id_kp_emailProtection + // Object Signing CA | true | id_kp_codeSigning + if (criticalNetscapeCertificateType.GetLength() > 0 && + (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) { + return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; + } } return der::End(tbsCertificate); } // XXX: The second value is of type |const Input&| instead of type |Input| due // to limitations in our std::bind polyfill. Result BackCert::RememberExtension(Reader& extnID, const Input& extnValue, - /*out*/ bool& understood) + bool critical, /*out*/ bool& understood) { understood = false; // python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15 static const uint8_t id_ce_keyUsage[] = { 0x55, 0x1d, 0x0f }; // python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17 @@ -200,16 +227,20 @@ BackCert::RememberExtension(Reader& extn // python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54 static const uint8_t id_ce_inhibitAnyPolicy[] = { 0x55, 0x1d, 0x36 }; // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1 static const uint8_t id_pe_authorityInfoAccess[] = { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }; + // python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1 + static const uint8_t Netscape_certificate_type[] = { + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 + }; Input* out = nullptr; // We already enforce the maximum possible constraints for policies so we // can safely ignore even critical policy constraint extensions. // // XXX: Doing it this way won't allow us to detect duplicate // policyConstraints extensions, but that's OK because (and only because) we @@ -233,16 +264,18 @@ BackCert::RememberExtension(Reader& extn } else if (extnID.MatchRest(id_ce_policyConstraints)) { out = &dummyPolicyConstraints; } else if (extnID.MatchRest(id_ce_extKeyUsage)) { out = &extKeyUsage; } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) { out = &inhibitAnyPolicy; } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) { out = &authorityInfoAccess; + } else if (extnID.MatchRest(Netscape_certificate_type) && critical) { + out = &criticalNetscapeCertificateType; } if (out) { // Don't allow an empty value for any extension we understand. This way, we // can test out->GetLength() != 0 or out->Init() to check for duplicates. if (extnValue.GetLength() == 0) { return Result::ERROR_EXTENSION_VALUE_INVALID; }
--- a/security/pkix/lib/pkixder.h +++ b/security/pkix/lib/pkixder.h @@ -565,17 +565,17 @@ OptionalExtensions(Reader& input, uint8_ return rv; } rv = End(extension); if (rv != Success) { return rv; } bool understood = false; - rv = extensionHandler(extnID, extnValue, understood); + rv = extensionHandler(extnID, extnValue, critical, understood); if (rv != Success) { return rv; } if (critical && !understood) { return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; } }
--- a/security/pkix/lib/pkixocsp.cpp +++ b/security/pkix/lib/pkixocsp.cpp @@ -151,17 +151,17 @@ static inline Result ResponseBytes(Reade static inline Result BasicResponse(Reader&, Context&); static inline Result ResponseData( Reader& tbsResponseData, Context& context, const SignedDataWithSignature& signedResponseData, const DERArray& certs); static inline Result SingleResponse(Reader& input, Context& context); static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue, - /*out*/ bool& understood); + bool critical, /*out*/ bool& understood); static inline Result CertID(Reader& input, const Context& context, /*out*/ bool& match); static Result MatchKeyHash(TrustDomain& trustDomain, Input issuerKeyHash, Input issuerSubjectPublicKeyInfo, /*out*/ bool& match); static Result KeyHash(TrustDomain& trustDomain, @@ -819,17 +819,17 @@ KeyHash(TrustDomain& trustDomain, const return rv; } return trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize); } Result ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/, - /*out*/ bool& understood) + bool /*critical*/, /*out*/ bool& understood) { understood = false; return Success; } // 1. The certificate identified in a received response corresponds to // the certificate that was identified in the corresponding request; // 2. The signature on the response is valid;
--- a/security/pkix/lib/pkixutil.h +++ b/security/pkix/lib/pkixutil.h @@ -129,19 +129,20 @@ private: Input authorityInfoAccess; Input basicConstraints; Input certificatePolicies; Input extKeyUsage; Input inhibitAnyPolicy; Input keyUsage; Input nameConstraints; Input subjectAltName; + Input criticalNetscapeCertificateType; Result RememberExtension(Reader& extnID, const Input& extnValue, - /*out*/ bool& understood); + bool critical, /*out*/ bool& understood); BackCert(const BackCert&) /* = delete */; void operator=(const BackCert&); /* = delete */; }; class NonOwningDERArray : public DERArray { public:
--- a/security/sandbox/mac/Sandbox.h +++ b/security/sandbox/mac/Sandbox.h @@ -30,16 +30,18 @@ typedef struct _MacSandboxPluginInfo { nsCString pluginBinaryPath; } MacSandboxPluginInfo; typedef struct _MacSandboxInfo { _MacSandboxInfo() : type(MacSandboxType_Default) {} MacSandboxType type; MacSandboxPluginInfo pluginInfo; + nsCString appPath; + nsCString appBinaryPath; } MacSandboxInfo; namespace mozilla { bool StartMacSandbox(MacSandboxInfo aInfo, nsCString &aErrorMessage); } // namespace mozilla
--- a/security/sandbox/mac/Sandbox.mm +++ b/security/sandbox/mac/Sandbox.mm @@ -28,36 +28,41 @@ static const char rules[] = " (global-name \"com.apple.cfprefsd.daemon\")\n" " (global-name \"com.apple.system.opendirectoryd.libinfo\")\n" " (global-name \"com.apple.system.logger\")\n" " (global-name \"com.apple.ls.boxd\"))\n" "(allow file-read*\n" " (regex #\"^/etc$\")\n" " (regex #\"^/dev/u?random$\")\n" " (regex #\"^/(private/)?var($|/)\")\n" - " (regex #\"\\.app/Contents/MacOS/plugin-container\\.app/Contents/\")\n" " (literal \"/usr/share/icu/icudt51l.dat\")\n" + " (literal \"%s\")\n" + " (literal \"%s\")\n" " (literal \"%s\"))\n"; bool StartMacSandbox(MacSandboxInfo aInfo, nsCString &aErrorMessage) { if (aInfo.type != MacSandboxType_Plugin) { aErrorMessage.AppendPrintf("Unexpected sandbox type %u", aInfo.type); return false; } nsAutoCString profile; if (nsCocoaFeatures::OnLionOrLater()) { profile.AppendPrintf(rules, ";", aInfo.pluginInfo.pluginPath.get(), - aInfo.pluginInfo.pluginBinaryPath.get()); + aInfo.pluginInfo.pluginBinaryPath.get(), + aInfo.appPath.get(), + aInfo.appBinaryPath.get()); } else { profile.AppendPrintf(rules, "", aInfo.pluginInfo.pluginPath.get(), - aInfo.pluginInfo.pluginBinaryPath.get()); + aInfo.pluginInfo.pluginBinaryPath.get(), + aInfo.appPath.get(), + aInfo.appBinaryPath.get()); } char *errorbuf = NULL; if (sandbox_init(profile.get(), 0, &errorbuf)) { if (errorbuf) { aErrorMessage.AppendPrintf("sandbox_init() failed with error \"%s\"", errorbuf); printf("profile: %s\n", profile.get());
--- a/toolkit/modules/BrowserUtils.jsm +++ b/toolkit/modules/BrowserUtils.jsm @@ -10,20 +10,19 @@ const {interfaces: Ci, utils: Cu, classe Cu.import("resource://gre/modules/Services.jsm"); this.BrowserUtils = { /** * Prints arguments separated by a space and appends a new line. */ - dumpLn: function () { - for (let i = 0; i < arguments.length; i++) { - dump(arguments[i] + " "); - } + dumpLn: function (...args) { + for (let a of args) + dump(a + " "); dump("\n"); }, /** * urlSecurityCheck: JavaScript wrapper for checkLoadURIWithPrincipal * and checkLoadURIStrWithPrincipal. * If |aPrincipal| is not allowed to link to |aURL|, this function throws with * an error message.
--- a/uriloader/base/nsURILoader.cpp +++ b/uriloader/base/nsURILoader.cpp @@ -583,16 +583,17 @@ nsresult nsDocumentOpenInfo::DispatchCon mContentType = APPLICATION_GUESS_FROM_EXT; aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT)); } rv = helperAppService->DoContent(mContentType, request, m_originalContext, false, + nullptr, getter_AddRefs(m_targetStreamListener)); if (NS_FAILED(rv)) { request->SetLoadFlags(loadFlags); m_targetStreamListener = nullptr; } } NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
--- a/uriloader/exthandler/ExternalHelperAppParent.cpp +++ b/uriloader/exthandler/ExternalHelperAppParent.cpp @@ -82,17 +82,18 @@ ExternalHelperAppParent::Init(ContentPar nsCOMPtr<nsIInterfaceRequestor> window; if (aBrowser) { TabParent* tabParent = static_cast<TabParent*>(aBrowser); if (tabParent->GetOwnerElement()) window = do_QueryInterface(tabParent->GetOwnerElement()->OwnerDoc()->GetWindow()); } helperAppService->DoContent(aMimeContentType, this, window, - aForceSave, getter_AddRefs(mListener)); + aForceSave, nullptr, + getter_AddRefs(mListener)); } void ExternalHelperAppParent::ActorDestroy(ActorDestroyReason why) { mIPCClosed = true; }
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -607,94 +607,117 @@ nsresult nsExternalHelperAppService::Ini NS_ENSURE_SUCCESS(rv, rv); return obs->AddObserver(this, "last-pb-context-exited", true); } nsExternalHelperAppService::~nsExternalHelperAppService() { } + +nsresult +nsExternalHelperAppService::DoContentContentProcessHelper(const nsACString& aMimeContentType, + nsIRequest *aRequest, + nsIInterfaceRequestor *aContentContext, + bool aForceSave, + nsIInterfaceRequestor *aWindowContext, + nsIStreamListener ** aStreamListener) +{ + nsCOMPtr<nsIDOMWindow> window = do_GetInterface(aContentContext); + NS_ENSURE_STATE(window); + + // We need to get a hold of a ContentChild so that we can begin forwarding + // this data to the parent. In the HTTP case, this is unfortunate, since + // we're actually passing data from parent->child->parent wastefully, but + // the Right Fix will eventually be to short-circuit those channels on the + // parent side based on some sort of subscription concept. + using mozilla::dom::ContentChild; + using mozilla::dom::ExternalHelperAppChild; + ContentChild *child = ContentChild::GetSingleton(); + if (!child) { + return NS_ERROR_FAILURE; + } + + nsCString disp; + nsCOMPtr<nsIURI> uri; + int64_t contentLength = -1; + uint32_t contentDisposition = -1; + nsAutoString fileName; + + nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); + if (channel) { + channel->GetURI(getter_AddRefs(uri)); + channel->GetContentLength(&contentLength); + channel->GetContentDisposition(&contentDisposition); + channel->GetContentDispositionFilename(fileName); + channel->GetContentDispositionHeader(disp); + } + + nsCOMPtr<nsIURI> referrer; + NS_GetReferrerFromChannel(channel, getter_AddRefs(referrer)); + + OptionalURIParams uriParams, referrerParams; + SerializeURI(uri, uriParams); + SerializeURI(referrer, referrerParams); + + // Now we build a protocol for forwarding our data to the parent. The + // protocol will act as a listener on the child-side and create a "real" + // helperAppService listener on the parent-side, via another call to + // DoContent. + mozilla::dom::PExternalHelperAppChild *pc = + child->SendPExternalHelperAppConstructor(uriParams, + nsCString(aMimeContentType), + disp, contentDisposition, + fileName, aForceSave, + contentLength, referrerParams, + mozilla::dom::TabChild::GetFrom(window)); + ExternalHelperAppChild *childListener = static_cast<ExternalHelperAppChild *>(pc); + + NS_ADDREF(*aStreamListener = childListener); + + uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE; + + nsRefPtr<nsExternalAppHandler> handler = + new nsExternalAppHandler(nullptr, EmptyCString(), aContentContext, aWindowContext, this, + fileName, reason, aForceSave); + if (!handler) { + return NS_ERROR_OUT_OF_MEMORY; + } + + childListener->SetHandler(handler); + return NS_OK; +} + NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeContentType, nsIRequest *aRequest, + nsIInterfaceRequestor *aContentContext, + bool aForceSave, nsIInterfaceRequestor *aWindowContext, - bool aForceSave, nsIStreamListener ** aStreamListener) { + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return DoContentContentProcessHelper(aMimeContentType, aRequest, aContentContext, + aForceSave, aWindowContext, aStreamListener); + } + nsAutoString fileName; nsAutoCString fileExtension; uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE; uint32_t contentDisposition = -1; - nsresult rv; - // Get the file extension and name that we will need later nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); nsCOMPtr<nsIURI> uri; int64_t contentLength = -1; if (channel) { channel->GetURI(getter_AddRefs(uri)); channel->GetContentLength(&contentLength); channel->GetContentDisposition(&contentDisposition); channel->GetContentDispositionFilename(fileName); - } - - if (XRE_GetProcessType() == GeckoProcessType_Content) { - nsCOMPtr<nsIDOMWindow> window = do_GetInterface(aWindowContext); - NS_ENSURE_STATE(window); - - // We need to get a hold of a ContentChild so that we can begin forwarding - // this data to the parent. In the HTTP case, this is unfortunate, since - // we're actually passing data from parent->child->parent wastefully, but - // the Right Fix will eventually be to short-circuit those channels on the - // parent side based on some sort of subscription concept. - using mozilla::dom::ContentChild; - using mozilla::dom::ExternalHelperAppChild; - ContentChild *child = ContentChild::GetSingleton(); - if (!child) - return NS_ERROR_FAILURE; - - nsCString disp; - if (channel) { - channel->GetContentDispositionHeader(disp); - } - - nsCOMPtr<nsIURI> referrer; - rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrer)); - - OptionalURIParams uriParams, referrerParams; - SerializeURI(uri, uriParams); - SerializeURI(referrer, referrerParams); - - // Now we build a protocol for forwarding our data to the parent. The - // protocol will act as a listener on the child-side and create a "real" - // helperAppService listener on the parent-side, via another call to - // DoContent. - mozilla::dom::PExternalHelperAppChild *pc = - child->SendPExternalHelperAppConstructor(uriParams, - nsCString(aMimeContentType), - disp, contentDisposition, - fileName, aForceSave, - contentLength, referrerParams, - mozilla::dom::TabChild::GetFrom(window)); - ExternalHelperAppChild *childListener = static_cast<ExternalHelperAppChild *>(pc); - - NS_ADDREF(*aStreamListener = childListener); - - nsRefPtr<nsExternalAppHandler> handler = - new nsExternalAppHandler(nullptr, EmptyCString(), aWindowContext, this, - fileName, - reason, aForceSave); - if (!handler) - return NS_ERROR_OUT_OF_MEMORY; - - childListener->SetHandler(handler); - return NS_OK; - } - - if (channel) { + // Check if we have a POST request, in which case we don't want to use // the url's extension bool allowURLExt = true; nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(channel); if (httpChan) { nsAutoCString requestMethod; httpChan->GetRequestMethod(requestMethod); allowURLExt = !requestMethod.EqualsLiteral("POST"); @@ -706,49 +729,53 @@ NS_IMETHODIMP nsExternalHelperAppService // bother checking the query if (uri && allowURLExt) { nsCOMPtr<nsIURL> url = do_QueryInterface(uri); if (url) { nsAutoCString query; // We only care about the query for HTTP and HTTPS URLs + nsresult rv; bool isHTTP, isHTTPS; rv = uri->SchemeIs("http", &isHTTP); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { isHTTP = false; + } rv = uri->SchemeIs("https", &isHTTPS); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { isHTTPS = false; - - if (isHTTP || isHTTPS) + } + if (isHTTP || isHTTPS) { url->GetQuery(query); + } // Only get the extension if the query is empty; if it isn't, then the // extension likely belongs to a cgi script and isn't helpful allowURLExt = query.IsEmpty(); } } // Extract name & extension bool isAttachment = GetFilenameAndExtensionFromChannel(channel, fileName, fileExtension, allowURLExt); LOG(("Found extension '%s' (filename is '%s', handling attachment: %i)", fileExtension.get(), NS_ConvertUTF16toUTF8(fileName).get(), isAttachment)); - if (isAttachment) + if (isAttachment) { reason = nsIHelperAppLauncherDialog::REASON_SERVERREQUEST; + } } LOG(("HelperAppService::DoContent: mime '%s', extension '%s'\n", PromiseFlatCString(aMimeContentType).get(), fileExtension.get())); - // we get the mime service here even though we're the default implementation of it, - // so it's possible to override only the mime service and not need to reimplement the - // whole external helper app service itself + // We get the mime service here even though we're the default implementation + // of it, so it's possible to override only the mime service and not need to + // reimplement the whole external helper app service itself. nsCOMPtr<nsIMIMEService> mimeSvc(do_GetService(NS_MIMESERVICE_CONTRACTID)); NS_ENSURE_TRUE(mimeSvc, NS_ERROR_FAILURE); // Try to find a mime object by looking at the mime type/extension nsCOMPtr<nsIMIMEInfo> mimeInfo; if (aMimeContentType.Equals(APPLICATION_GUESS_FROM_EXT, nsCaseInsensitiveCStringComparator())) { nsAutoCString mimeType; if (!fileExtension.IsEmpty()) { @@ -762,49 +789,55 @@ NS_IMETHODIMP nsExternalHelperAppService } if (fileExtension.IsEmpty() || mimeType.IsEmpty()) { // Extension lookup gave us no useful match mimeSvc->GetFromTypeAndExtension(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM), fileExtension, getter_AddRefs(mimeInfo)); mimeType.AssignLiteral(APPLICATION_OCTET_STREAM); } - if (channel) + + if (channel) { channel->SetContentType(mimeType); + } + // Don't overwrite SERVERREQUEST - if (reason == nsIHelperAppLauncherDialog::REASON_CANTHANDLE) + if (reason == nsIHelperAppLauncherDialog::REASON_CANTHANDLE) { reason = nsIHelperAppLauncherDialog::REASON_TYPESNIFFED; - } - else { + } + } else { mimeSvc->GetFromTypeAndExtension(aMimeContentType, fileExtension, getter_AddRefs(mimeInfo)); } LOG(("Type/Ext lookup found 0x%p\n", mimeInfo.get())); // No mimeinfo -> we can't continue. probably OOM. - if (!mimeInfo) + if (!mimeInfo) { return NS_ERROR_OUT_OF_MEMORY; + } *aStreamListener = nullptr; // We want the mimeInfo's primary extension to pass it to // nsExternalAppHandler nsAutoCString buf; mimeInfo->GetPrimaryExtension(buf); nsExternalAppHandler * handler = new nsExternalAppHandler(mimeInfo, buf, + aContentContext, aWindowContext, this, fileName, reason, aForceSave); - if (!handler) + if (!handler) { return NS_ERROR_OUT_OF_MEMORY; + } + NS_ADDREF(*aStreamListener = handler); - return NS_OK; } NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(const nsACString& aExtension, const nsACString& aEncodingType, bool *aApplyDecoding) { *aApplyDecoding = true; @@ -1149,21 +1182,23 @@ NS_INTERFACE_MAP_BEGIN(nsExternalAppHand NS_INTERFACE_MAP_ENTRY(nsIHelperAppLauncher) NS_INTERFACE_MAP_ENTRY(nsICancelable) NS_INTERFACE_MAP_ENTRY(nsITimerCallback) NS_INTERFACE_MAP_ENTRY(nsIBackgroundFileSaverObserver) NS_INTERFACE_MAP_END_THREADSAFE nsExternalAppHandler::nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsCSubstring& aTempFileExtension, + nsIInterfaceRequestor* aContentContext, nsIInterfaceRequestor* aWindowContext, nsExternalHelperAppService *aExtProtSvc, const nsAString& aSuggestedFilename, uint32_t aReason, bool aForceSave) : mMimeInfo(aMIMEInfo) +, mContentContext(aContentContext) , mWindowContext(aWindowContext) , mWindowToClose(nullptr) , mSuggestedFileName(aSuggestedFilename) , mForceSave(aForceSave) , mCanceled(false) , mShouldCloseWindow(false) , mStopRequestIssued(false) , mReason(aReason) @@ -1281,27 +1316,29 @@ void nsExternalAppHandler::RetargetLoadN // ideally we should be able to just use mChannel (the channel we are extracting content from) or // the default load channel associated with the original load group. Unfortunately because // a redirect may have occurred, the doc loader is the only one with a ptr to the original channel // which is what we really want.... // Note that we need to do this before removing aChannel from the loadgroup, // since that would mess with the original channel on the loader. nsCOMPtr<nsIDocumentLoader> origContextLoader = - do_GetInterface(mWindowContext); - if (origContextLoader) + do_GetInterface(mContentContext); + if (origContextLoader) { origContextLoader->GetDocumentChannel(getter_AddRefs(mOriginalChannel)); + } bool isPrivate = NS_UsePrivateBrowsing(aChannel); nsCOMPtr<nsILoadGroup> oldLoadGroup; aChannel->GetLoadGroup(getter_AddRefs(oldLoadGroup)); - if(oldLoadGroup) - oldLoadGroup->RemoveRequest(request, nullptr, NS_BINDING_RETARGETED); + if(oldLoadGroup) { + oldLoadGroup->RemoveRequest(request, nullptr, NS_BINDING_RETARGETED); + } aChannel->SetLoadGroup(nullptr); aChannel->SetNotificationCallbacks(nullptr); nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(aChannel); if (pbChannel) { pbChannel->SetPrivate(isPrivate); } @@ -1486,18 +1523,17 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt if (props) { bool tmp = false; props->GetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"), &tmp); mShouldCloseWindow = tmp; } // Now get the URI - if (aChannel) - { + if (aChannel) { aChannel->GetURI(getter_AddRefs(mSourceUrl)); } // retarget all load notifications to our docloader instead of the original window's docloader... RetargetLoadNotifications(request); // Check to see if there is a refresh header on the original channel. if (mOriginalChannel) { @@ -1518,55 +1554,50 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt // In an IPC setting, we're allowing the child process, here, to make // decisions about decoding the channel (e.g. decompression). It will // still forward the decoded (uncompressed) data back to the parent. // Con: Uncompressed data means more IPC overhead. // Pros: ExternalHelperAppParent doesn't need to implement nsIEncodedChannel. // Parent process doesn't need to expect CPU time on decompression. nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface( aChannel ); - if (encChannel) - { + if (encChannel) { // Turn off content encoding conversions if needed bool applyConversion = true; nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(mSourceUrl)); - if (sourceURL) - { + if (sourceURL) { nsAutoCString extension; sourceURL->GetFileExtension(extension); - if (!extension.IsEmpty()) - { + if (!extension.IsEmpty()) { nsCOMPtr<nsIUTF8StringEnumerator> encEnum; encChannel->GetContentEncodings(getter_AddRefs(encEnum)); - if (encEnum) - { + if (encEnum) { bool hasMore; rv = encEnum->HasMore(&hasMore); - if (NS_SUCCEEDED(rv) && hasMore) - { + if (NS_SUCCEEDED(rv) && hasMore) { nsAutoCString encType; rv = encEnum->GetNext(encType); - if (NS_SUCCEEDED(rv) && !encType.IsEmpty()) - { + if (NS_SUCCEEDED(rv) && !encType.IsEmpty()) { mExtProtSvc->ApplyDecodingForExtension(extension, encType, &applyConversion); } } } } } encChannel->SetApplyConversion( applyConversion ); } // At this point, the child process has done everything it can usefully do // for OnStartRequest. - if (XRE_GetProcessType() == GeckoProcessType_Content) - return NS_OK; + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return NS_OK; + } rv = SetUpTempFile(aChannel); if (NS_FAILED(rv)) { nsresult transferError = rv; rv = CreateFailedTransfer(aChannel && NS_UsePrivateBrowsing(aChannel)); #ifdef PR_LOGGING if (NS_FAILED(rv)) { @@ -1604,42 +1635,37 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt // is one part of a multipart document). Opening sniffed content in helper // apps by default introduces security holes that we'd rather not have. // So let's find out whether the user wants to be prompted. If he does not, // check mReason and the preferred action to see what we should do. bool alwaysAsk = true; mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk); - if (alwaysAsk) - { + if (alwaysAsk) { // But we *don't* ask if this mimeInfo didn't come from // our user configuration datastore and the user has said // at some point in the distant past that they don't // want to be asked. The latter fact would have been // stored in pref strings back in the old days. bool mimeTypeIsInDatastore = false; nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID); - if (handlerSvc) + if (handlerSvc) { handlerSvc->Exists(mMimeInfo, &mimeTypeIsInDatastore); - if (!handlerSvc || !mimeTypeIsInDatastore) - { + } + if (!handlerSvc || !mimeTypeIsInDatastore) { nsAutoCString MIMEType; mMimeInfo->GetMIMEType(MIMEType); - - if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_SAVE_TO_DISK_PREF, MIMEType.get())) - { + if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_SAVE_TO_DISK_PREF, MIMEType.get())) { // Don't need to ask after all. alwaysAsk = false; // Make sure action matches pref (save to disk). mMimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk); - } - else if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_OPEN_FILE_PREF, MIMEType.get())) - { + } else if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_OPEN_FILE_PREF, MIMEType.get())) { // Don't need to ask after all. alwaysAsk = false; } } } int32_t action = nsIMIMEInfo::saveToDisk; mMimeInfo->GetPreferredAction( &action ); @@ -1655,23 +1681,23 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt // before this is irrelevant; override it if (mForceSave) { alwaysAsk = false; action = nsIMIMEInfo::saveToDisk; } if (alwaysAsk) { - // invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request - mDialog = do_CreateInstance( NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv ); + // Display the dialog + mDialog = do_CreateInstance(NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // this will create a reference cycle (the dialog holds a reference to us as // nsIHelperAppLauncher), which will be broken in Cancel or CreateTransfer. - rv = mDialog->Show( this, mWindowContext, mReason ); + rv = mDialog->Show(this, GetDialogParent(), mReason); // what do we do if the dialog failed? I guess we should call Cancel and abort the load.... } else { // We need to do the save/open immediately, then. #ifdef XP_WIN @@ -1698,36 +1724,32 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt } else { // Paranoia is good here too, though this really should not happen NS_WARNING("GetDownloadInfo returned a null file after the temp file has been set up! "); action = nsIMIMEInfo::saveToDisk; } } #endif if (action == nsIMIMEInfo::useHelperApp || - action == nsIMIMEInfo::useSystemDefault) - { + action == nsIMIMEInfo::useSystemDefault) { rv = LaunchWithApplication(nullptr, false); - } - else // Various unknown actions go here too - { + } else { rv = SaveToDisk(nullptr, false); } } return NS_OK; } // Convert error info into proper message text and send OnStatusChange // notification to the dialog progress listener or nsITransfer implementation. void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequest *aRequest, const nsAFlatString &path) { nsAutoString msgId; - switch(rv) - { + switch (rv) { case NS_ERROR_OUT_OF_MEMORY: // No memory msgId.AssignLiteral("noMemory"); break; case NS_ERROR_FILE_DISK_FULL: case NS_ERROR_FILE_NO_DEVICE_SPACE: // Out of space on target volume. @@ -1744,19 +1766,17 @@ void nsExternalAppHandler::SendStatusCha // Attempt to write without sufficient permissions. #if defined(ANDROID) // On Android (and Gonk), this means the SD card is present but // unavailable (read-only). msgId.AssignLiteral("SDAccessErrorCardReadOnly"); #else msgId.AssignLiteral("accessError"); #endif - } - else - { + } else { msgId.AssignLiteral("launchError"); } break; case NS_ERROR_FILE_NOT_FOUND: case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST: case NS_ERROR_FILE_UNRECOGNIZED_PATH: // Helper app not found, let's verify this happened on launch @@ -1771,18 +1791,17 @@ void nsExternalAppHandler::SendStatusCha msgId.AssignLiteral("SDAccessErrorCardMissing"); break; } #endif // fall through default: // Generic read/write/launch error message. - switch(type) - { + switch (type) { case kReadError: msgId.AssignLiteral("readError"); break; case kWriteError: msgId.AssignLiteral("writeError"); break; case kLaunchError: msgId.AssignLiteral("launchError"); @@ -1794,76 +1813,69 @@ void nsExternalAppHandler::SendStatusCha ("Error: %s, type=%i, listener=0x%p, transfer=0x%p, rv=0x%08X\n", NS_LossyConvertUTF16toASCII(msgId).get(), type, mDialogProgressListener.get(), mTransfer.get(), rv)); PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_ERROR, (" path='%s'\n", NS_ConvertUTF16toUTF8(path).get())); // Get properties file bundle and extract status string. nsCOMPtr<nsIStringBundleService> stringService = mozilla::services::GetStringBundleService(); - if (stringService) - { + if (stringService) { nsCOMPtr<nsIStringBundle> bundle; - if (NS_SUCCEEDED(stringService->CreateBundle("chrome://global/locale/nsWebBrowserPersist.properties", getter_AddRefs(bundle)))) - { + if (NS_SUCCEEDED(stringService->CreateBundle("chrome://global/locale/nsWebBrowserPersist.properties", + getter_AddRefs(bundle)))) { nsXPIDLString msgText; const char16_t *strings[] = { path.get() }; - if(NS_SUCCEEDED(bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText)))) - { - if (mDialogProgressListener) - { + if (NS_SUCCEEDED(bundle->FormatStringFromName(msgId.get(), strings, 1, + getter_Copies(msgText)))) { + if (mDialogProgressListener) { // We have a listener, let it handle the error. mDialogProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText); } else if (mTransfer) { mTransfer->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText); - } - else - if (XRE_GetProcessType() == GeckoProcessType_Default) { + } else if (XRE_GetProcessType() == GeckoProcessType_Default) { // We don't have a listener. Simply show the alert ourselves. nsresult qiRv; - nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mWindowContext, &qiRv)); + nsCOMPtr<nsIPrompt> prompter(do_GetInterface(GetDialogParent(), &qiRv)); nsXPIDLString title; bundle->FormatStringFromName(MOZ_UTF16("title"), strings, 1, getter_Copies(title)); PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_DEBUG, - ("mWindowContext=0x%p, prompter=0x%p, qi rv=0x%08X, title='%s', msg='%s'", - mWindowContext.get(), + ("mContentContext=0x%p, prompter=0x%p, qi rv=0x%08X, title='%s', msg='%s'", + mContentContext.get(), prompter.get(), qiRv, NS_ConvertUTF16toUTF8(title).get(), NS_ConvertUTF16toUTF8(msgText).get())); // If we didn't have a prompter we will try and get a window // instead, get it's docshell and use it to alert the user. - if (!prompter) - { - nsCOMPtr<nsPIDOMWindow> window(do_GetInterface(mWindowContext)); - if (!window || !window->GetDocShell()) - { + if (!prompter) { + nsCOMPtr<nsPIDOMWindow> window(do_GetInterface(GetDialogParent())); + if (!window || !window->GetDocShell()) { return; } prompter = do_GetInterface(window->GetDocShell(), &qiRv); PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_DEBUG, - ("No prompter from mWindowContext, using DocShell, " \ + ("No prompter from mContentContext, using DocShell, " \ "window=0x%p, docShell=0x%p, " \ "prompter=0x%p, qi rv=0x%08X", window.get(), window->GetDocShell(), prompter.get(), qiRv)); // If we still don't have a prompter, there's nothing else we // can do so just return. - if (!prompter) - { + if (!prompter) { PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_ERROR, ("No prompter from DocShell, no way to alert user")); return; } } // We should always have a prompter at this point. prompter->Alert(title, msgText); @@ -1875,41 +1887,40 @@ void nsExternalAppHandler::SendStatusCha NS_IMETHODIMP nsExternalAppHandler::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt, nsIInputStream * inStr, uint64_t sourceOffset, uint32_t count) { nsresult rv = NS_OK; // first, check to see if we've been canceled.... - if (mCanceled || !mSaver) // then go cancel our underlying channel too + if (mCanceled || !mSaver) { + // then go cancel our underlying channel too return request->Cancel(NS_BINDING_ABORTED); + } // read the data out of the stream and write it to the temp file. - if (count > 0) - { + if (count > 0) { mProgress += count; nsCOMPtr<nsIStreamListener> saver = do_QueryInterface(mSaver); rv = saver->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count); - if (NS_SUCCEEDED(rv)) - { + if (NS_SUCCEEDED(rv)) { // Send progress notification. if (mTransfer) { mTransfer->OnProgressChange64(nullptr, request, mProgress, mContentLength, mProgress, mContentLength); } - } - else - { + } else { // An error occurred, notify listener. nsAutoString tempFilePath; - if (mTempFile) + if (mTempFile) { mTempFile->GetPath(tempFilePath); + } SendStatusChange(kReadError, rv, request, tempFilePath); // Cancel the download. Cancel(rv); } } return rv; } @@ -1919,30 +1930,30 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt { LOG(("nsExternalAppHandler::OnStopRequest\n" " mCanceled=%d, mTransfer=0x%p, aStatus=0x%08X\n", mCanceled, mTransfer.get(), aStatus)); mStopRequestIssued = true; // Cancel if the request did not complete successfully. - if (!mCanceled && NS_FAILED(aStatus)) - { + if (!mCanceled && NS_FAILED(aStatus)) { // Send error notification. nsAutoString tempFilePath; if (mTempFile) mTempFile->GetPath(tempFilePath); SendStatusChange( kReadError, aStatus, request, tempFilePath ); Cancel(aStatus); } // first, check to see if we've been canceled.... - if (mCanceled || !mSaver) + if (mCanceled || !mSaver) { return NS_OK; + } return mSaver->Finish(NS_OK); } NS_IMETHODIMP nsExternalAppHandler::OnTargetChange(nsIBackgroundFileSaver *aSaver, nsIFile *aTarget) { @@ -2178,24 +2189,23 @@ nsresult nsExternalAppHandler::SaveDesti else Cancel(NS_BINDING_ABORTED); return NS_OK; } void nsExternalAppHandler::RequestSaveDestination(const nsAFlatString &aDefaultFile, const nsAFlatString &aFileExtension) { - // invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request - // Convert to use file picker? No, then embeddors could not do any sort of + // Display the dialog + // XXX Convert to use file picker? No, then embeddors could not do any sort of // "AutoDownload" w/o showing a prompt nsresult rv = NS_OK; - if (!mDialog) - { + if (!mDialog) { // Get helper app launcher dialog. - mDialog = do_CreateInstance( NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv ); + mDialog = do_CreateInstance(NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv); if (rv != NS_OK) { Cancel(NS_BINDING_ABORTED); return; } } // we want to explicitly unescape aDefaultFile b4 passing into the dialog. we can't unescape // it because the dialog is implemented by a JS component which doesn't have a window so no unescape routine is defined... @@ -2204,25 +2214,25 @@ void nsExternalAppHandler::RequestSaveDe // If we don't do this, users that close the helper app dialog while the file // picker is up would cause Cancel() to be called, and the dialog would be // released, which would release this object too, which would crash. // See Bug 249143 nsIFile* fileToUse; nsRefPtr<nsExternalAppHandler> kungFuDeathGrip(this); nsCOMPtr<nsIHelperAppLauncherDialog> dlg(mDialog); rv = mDialog->PromptForSaveToFile(this, - mWindowContext, + GetDialogParent(), aDefaultFile.get(), aFileExtension.get(), mForceSave, &fileToUse); if (rv == NS_ERROR_NOT_AVAILABLE) { // we need to use the async version -> nsIHelperAppLauncherDialog.promptForSaveToFileAsync. rv = mDialog->PromptForSaveToFileAsync(this, - mWindowContext, + GetDialogParent(), aDefaultFile.get(), aFileExtension.get(), mForceSave); } else { SaveDestinationAvailable(rv == NS_OK ? fileToUse : nullptr); } } @@ -2329,24 +2339,22 @@ NS_IMETHODIMP nsExternalAppHandler::Laun PlatformLocalHandlerApp_t *handlerApp = new PlatformLocalHandlerApp_t(EmptyString(), aApplication); mMimeInfo->SetPreferredApplicationHandler(handlerApp); } // Now check if the file is local, in which case we won't bother with saving // it to a temporary directory and just launch it from where it is nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(mSourceUrl)); - if (fileUrl && mIsFileChannel) - { + if (fileUrl && mIsFileChannel) { Cancel(NS_BINDING_ABORTED); nsCOMPtr<nsIFile> file; nsresult rv = fileUrl->GetFile(getter_AddRefs(file)); - if (NS_SUCCEEDED(rv)) - { + if (NS_SUCCEEDED(rv)) { rv = mMimeInfo->LaunchWithFile(file); if (NS_SUCCEEDED(rv)) return NS_OK; } nsAutoString path; if (file) file->GetPath(path); // If we get here, an error happened @@ -2359,40 +2367,36 @@ NS_IMETHODIMP nsExternalAppHandler::Laun // was specified in mSuggestedFileName after the download is done prior to // launching the helper app. So that any existing file of that name won't be // overwritten we call CreateUnique(). Also note that we use the same // directory as originally downloaded so nsDownload can rename in place // later. nsCOMPtr<nsIFile> fileToUse; (void) GetDownloadDirectory(getter_AddRefs(fileToUse)); - if (mSuggestedFileName.IsEmpty()) - { + if (mSuggestedFileName.IsEmpty()) { // Keep using the leafname of the temp file, since we're just starting a helper mSuggestedFileName = mTempLeafName; } #ifdef XP_WIN fileToUse->Append(mSuggestedFileName + mTempFileExtension); #else fileToUse->Append(mSuggestedFileName); #endif nsresult rv = fileToUse->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); - if(NS_SUCCEEDED(rv)) - { + if(NS_SUCCEEDED(rv)) { mFinalFileDestination = do_QueryInterface(fileToUse); // launch the progress window now that the user has picked the desired action. rv = CreateTransfer(); if (NS_FAILED(rv)) { Cancel(rv); } - } - else - { + } else { // Cancel the download and report an error. We do not want to end up in // a state where it appears that we have a normal download that is // pointing to a file that we did not actually create. nsAutoString path; mTempFile->GetPath(path); SendStatusChange(kWriteError, rv, nullptr, path); Cancel(rv); } @@ -2446,19 +2450,18 @@ void nsExternalAppHandler::ProcessAnyRef { // one last thing, try to see if the original window context supports a refresh interface... // Sometimes, when you download content that requires an external handler, there is // a refresh header associated with the download. This refresh header points to a page // the content provider wants the user to see after they download the content. How do we // pass this refresh information back to the caller? For now, try to get the refresh URI // interface. If the window context where the request originated came from supports this // then we can force it to process the refresh information (if there is any) from this channel. - if (mWindowContext && mOriginalChannel) - { - nsCOMPtr<nsIRefreshURI> refreshHandler (do_GetInterface(mWindowContext)); + if (mContentContext && mOriginalChannel) { + nsCOMPtr<nsIRefreshURI> refreshHandler (do_GetInterface(mContentContext)); if (refreshHandler) { refreshHandler->SetupRefreshURI(mOriginalChannel); } mOriginalChannel = nullptr; } } bool nsExternalAppHandler::GetNeverAskFlagFromPref(const char * prefName, const char * aContentType) @@ -2475,28 +2478,28 @@ bool nsExternalAppHandler::GetNeverAskFl prefCString.BeginReading(start); prefCString.EndReading(end); return !CaseInsensitiveFindInReadable(nsDependentCString(aContentType), start, end); } nsresult nsExternalAppHandler::MaybeCloseWindow() { - nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mWindowContext); + nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mContentContext); NS_ENSURE_STATE(window); if (mShouldCloseWindow) { // Reset the window context to the opener window so that the dependent // dialogs have a parent nsCOMPtr<nsIDOMWindow> opener; window->GetOpener(getter_AddRefs(opener)); bool isClosed; if (opener && NS_SUCCEEDED(opener->GetClosed(&isClosed)) && !isClosed) { - mWindowContext = do_GetInterface(opener); + mContentContext = do_GetInterface(opener); // Now close the old window. Do it on a timer so that we don't run // into issues trying to close the window before it has fully opened. NS_ASSERTION(!mTimer, "mTimer was already initialized once!"); mTimer = do_CreateInstance("@mozilla.org/timer;1"); if (!mTimer) { return NS_ERROR_FAILURE; }
--- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -181,16 +181,24 @@ protected: * Array for the files that should be deleted */ nsCOMArray<nsIFile> mTemporaryFilesList; /** * Array for the files that should be deleted (for the temporary files * added during the private browsing mode) */ nsCOMArray<nsIFile> mTemporaryPrivateFilesList; + +private: + nsresult DoContentContentProcessHelper(const nsACString& aMimeContentType, + nsIRequest *aRequest, + nsIInterfaceRequestor *aContentContext, + bool aForceSave, + nsIInterfaceRequestor *aWindowContext, + nsIStreamListener ** aStreamListener); }; /** * An external app handler is just a small little class that presents itself as * a nsIStreamListener. It saves the incoming data into a temp file. The handler * is bound to an application when it is created. When it receives an * OnStopRequest it launches the application using the temp file it has * stored the data into. We create a handler every time we have to process @@ -206,49 +214,68 @@ public: NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSIHELPERAPPLAUNCHER NS_DECL_NSICANCELABLE NS_DECL_NSITIMERCALLBACK NS_DECL_NSIBACKGROUNDFILESAVEROBSERVER /** - * @param aMIMEInfo MIMEInfo object, representing the type of the - * content that should be handled - * @param aFileExtension The extension we need to append to our temp file, - * INCLUDING the ".". e.g. .mp3 - * @param aWindowContext Window context, as passed to DoContent - * @param mExtProtSvc nsExternalHelperAppService on creation - * @param aFileName The filename to use - * @param aReason A constant from nsIHelperAppLauncherDialog indicating - * why the request is handled by a helper app. + * @param aMIMEInfo MIMEInfo object, representing the type of the + * content that should be handled + * @param aFileExtension The extension we need to append to our temp file, + * INCLUDING the ".". e.g. .mp3 + * @param aContentContext dom Window context, as passed to DoContent. + * @param aWindowContext Top level window context used in dialog parenting, + * as passed to DoContent. This parameter may be null, + * in which case dialogs will be parented to + * aContentContext. + * @param mExtProtSvc nsExternalHelperAppService on creation + * @param aFileName The filename to use + * @param aReason A constant from nsIHelperAppLauncherDialog indicating + * why the request is handled by a helper app. */ nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsCSubstring& aFileExtension, + nsIInterfaceRequestor * aContentContext, nsIInterfaceRequestor * aWindowContext, nsExternalHelperAppService * aExtProtSvc, const nsAString& aFilename, uint32_t aReason, bool aForceSave); /** * Clean up after the request was diverted to the parent process. */ void DidDivertRequest(nsIRequest *request); protected: ~nsExternalAppHandler(); + nsIInterfaceRequestor* GetDialogParent() { + return mWindowContext ? mWindowContext : mContentContext; + } + nsCOMPtr<nsIFile> mTempFile; nsCOMPtr<nsIURI> mSourceUrl; nsString mTempFileExtension; nsString mTempLeafName; /** * The MIME Info for this load. Will never be null. */ nsCOMPtr<nsIMIMEInfo> mMimeInfo; + + /** + * The dom window associated with this request to handle content. + */ + nsCOMPtr<nsIInterfaceRequestor> mContentContext; + + /** + * If set, the parent window helper app dialogs and file pickers + * should use in parenting. If null, we use mContentContext. + */ nsCOMPtr<nsIInterfaceRequestor> mWindowContext; /** * Used to close the window on a timer, to avoid any exceptions that are * thrown if we try to close the window before it's fully loaded. */ nsCOMPtr<nsIDOMWindow> mWindowToClose; nsCOMPtr<nsITimer> mTimer;
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl +++ b/uriloader/exthandler/nsIExternalHelperAppService.idl @@ -13,36 +13,41 @@ interface nsIFile; interface nsIMIMEInfo; interface nsIWebProgressListener2; interface nsIInterfaceRequestor; /** * The external helper app service is used for finding and launching * platform specific external applications for a given mime content type. */ -[scriptable, uuid(9e456297-ba3e-42b1-92bd-b7db014268cb)] +[scriptable, uuid(1E4F3AE1-B737-431F-A95D-31FA8DA70199)] interface nsIExternalHelperAppService : nsISupports { /** * Binds an external helper application to a stream listener. The caller * should pump data into the returned stream listener. When the OnStopRequest * is issued, the stream listener implementation will launch the helper app * with this data. * @param aMimeContentType The content type of the incoming data * @param aRequest The request corresponding to the incoming data - * @param aWindowContext Use GetInterface to retrieve properties like the - * dom window or parent window... - * The service might need this in order to bring up dialogs. + * @param aContentContext Used in processing content document refresh + * headers after target content is downloaded. Note in e10s land + * this is likely a CPOW that points to a window in the child process. * @param aForceSave True to always save this content to disk, regardless of - * nsIMIMEInfo and other such influences. + * nsIMIMEInfo and other such influences. + * @param aWindowContext Used in parenting helper app dialogs, usually + * points to the parent browser window. This parameter may be null, + * in which case dialogs will be parented to aContentContext. * @return A nsIStreamListener which the caller should pump the data into. */ - nsIStreamListener doContent (in ACString aMimeContentType, in nsIRequest aRequest, - in nsIInterfaceRequestor aWindowContext, - in boolean aForceSave); + nsIStreamListener doContent (in ACString aMimeContentType, + in nsIRequest aRequest, + in nsIInterfaceRequestor aContentContext, + in boolean aForceSave, + [optional] in nsIInterfaceRequestor aWindowContext); /** * Returns true if data from a URL with this extension combination * is to be decoded from aEncodingType prior to saving or passing * off to helper apps, false otherwise. */ boolean applyDecodingForExtension(in AUTF8String aExtension, in ACString aEncodingType);
--- a/widget/windows/nsColorPicker.cpp +++ b/widget/windows/nsColorPicker.cpp @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsColorPicker.h" #include <shlwapi.h> #include "mozilla/AutoRestore.h" #include "nsIWidget.h" +#include "nsString.h" #include "WidgetUtils.h" using namespace mozilla::widget; namespace { // Manages NS_NATIVE_TMP_WINDOW child windows. NS_NATIVE_TMP_WINDOWs are // temporary child windows of mParentWidget created to address RTL issues @@ -85,65 +86,101 @@ BGRIntToRGBString(DWORD color, nsAString aResult.Assign('#'); aResult.Append(ToHexString(r)); aResult.Append(ToHexString(g)); aResult.Append(ToHexString(b)); } } // anonymous namespace -AsyncColorChooser::AsyncColorChooser(const nsAString& aInitialColor, +static AsyncColorChooser* gColorChooser; + +AsyncColorChooser::AsyncColorChooser(COLORREF aInitialColor, nsIWidget* aParentWidget, nsIColorPickerShownCallback* aCallback) : mInitialColor(aInitialColor) + , mColor(aInitialColor) , mParentWidget(aParentWidget) , mCallback(aCallback) { } NS_IMETHODIMP AsyncColorChooser::Run() { static COLORREF sCustomColors[16] = {0} ; MOZ_ASSERT(NS_IsMainThread(), "Color pickers can only be opened from main thread currently"); - static bool sColorPickerOpen = false; // Allow only one color picker to be opened at a time, to workaround bug 944737 - if (!sColorPickerOpen) { - mozilla::AutoRestore<bool> autoRestoreColorPickerOpen(sColorPickerOpen); - sColorPickerOpen = true; + if (!gColorChooser) { + mozilla::AutoRestore<AsyncColorChooser*> restoreColorChooser(gColorChooser); + gColorChooser = this; AutoDestroyTmpWindow adtw((HWND) (mParentWidget.get() ? mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr)); CHOOSECOLOR options; options.lStructSize = sizeof(options); options.hwndOwner = adtw.get(); - options.Flags = CC_RGBINIT | CC_FULLOPEN; - options.rgbResult = ColorStringToRGB(mInitialColor); + options.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ENABLEHOOK; + options.rgbResult = mInitialColor; options.lpCustColors = sCustomColors; + options.lpfnHook = HookProc; - if (ChooseColor(&options)) { - BGRIntToRGBString(options.rgbResult, mColor); - } + mColor = ChooseColor(&options) ? options.rgbResult : mInitialColor; } else { NS_WARNING("Currently, it's not possible to open more than one color " "picker at a time"); mColor = mInitialColor; } if (mCallback) { - mCallback->Done(mColor); + nsAutoString colorStr; + BGRIntToRGBString(mColor, colorStr); + mCallback->Done(colorStr); } return NS_OK; } +void +AsyncColorChooser::Update(COLORREF aColor) +{ + if (mColor != aColor) { + mColor = aColor; + + nsAutoString colorStr; + BGRIntToRGBString(mColor, colorStr); + mCallback->Update(colorStr); + } +} + +/* static */ UINT_PTR CALLBACK +AsyncColorChooser::HookProc(HWND aDialog, UINT aMsg, + WPARAM aWParam, LPARAM aLParam) +{ + if (!gColorChooser) { + return 0; + } + + if (aMsg == WM_CTLCOLORSTATIC) { + // The color picker does not expose a proper way to retrieve the current + // color, so we need to obtain it from the static control displaying the + // current color instead. + const int kCurrentColorBoxID = 709; + if ((HWND)aLParam == GetDlgItem(aDialog, kCurrentColorBoxID)) { + gColorChooser->Update(GetPixel((HDC)aWParam, 0, 0)); + } + } + + return 0; +} + /////////////////////////////////////////////////////////////////////////////// // nsIColorPicker nsColorPicker::nsColorPicker() { } nsColorPicker::~nsColorPicker() @@ -155,19 +192,21 @@ NS_IMPL_ISUPPORTS(nsColorPicker, nsIColo NS_IMETHODIMP nsColorPicker::Init(nsIDOMWindow* parent, const nsAString& title, const nsAString& aInitialColor) { NS_PRECONDITION(parent, "Null parent passed to colorpicker, no color picker for you!"); mParentWidget = WidgetUtils::DOMWindowToWidget(parent); - mInitialColor = aInitialColor; + mInitialColor = ColorStringToRGB(aInitialColor); return NS_OK; } NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback* aCallback) { NS_ENSURE_ARG(aCallback); - nsCOMPtr<nsIRunnable> event = new AsyncColorChooser(mInitialColor, mParentWidget, aCallback); + nsCOMPtr<nsIRunnable> event = new AsyncColorChooser(mInitialColor, + mParentWidget, + aCallback); return NS_DispatchToMainThread(event); }
--- a/widget/windows/nsColorPicker.h +++ b/widget/windows/nsColorPicker.h @@ -7,49 +7,53 @@ #ifndef nsColorPicker_h__ #define nsColorPicker_h__ #include <windows.h> #include <commdlg.h> #include "nsCOMPtr.h" #include "nsIColorPicker.h" -#include "nsString.h" #include "nsThreadUtils.h" class nsIWidget; class AsyncColorChooser : public nsRunnable { public: - AsyncColorChooser(const nsAString& aInitialColor, + AsyncColorChooser(COLORREF aInitialColor, nsIWidget* aParentWidget, nsIColorPickerShownCallback* aCallback); NS_IMETHOD Run() MOZ_OVERRIDE; private: - nsString mInitialColor; + void Update(COLORREF aColor); + + static UINT_PTR CALLBACK HookProc(HWND aDialog, UINT aMsg, + WPARAM aWParam, LPARAM aLParam); + + COLORREF mInitialColor; + COLORREF mColor; nsCOMPtr<nsIWidget> mParentWidget; nsCOMPtr<nsIColorPickerShownCallback> mCallback; - nsString mColor; }; class nsColorPicker : public nsIColorPicker { virtual ~nsColorPicker(); public: nsColorPicker(); NS_DECL_ISUPPORTS NS_IMETHOD Init(nsIDOMWindow* parent, const nsAString& title, const nsAString& aInitialColor); NS_IMETHOD Open(nsIColorPickerShownCallback* aCallback); -protected: - nsString mInitialColor; +private: + COLORREF mInitialColor; nsCOMPtr<nsIWidget> mParentWidget; }; #endif // nsColorPicker_h__
--- a/xpcom/base/AvailableMemoryTracker.cpp +++ b/xpcom/base/AvailableMemoryTracker.cpp @@ -144,28 +144,26 @@ bool sHooksActive = false; // Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires // a lock! volatile bool sHasScheduledOneLowMemoryNotification = false; volatile PRIntervalTime sLastLowMemoryNotificationTime; // These are function pointers to the functions we wrap in Init(). -void* (WINAPI* sVirtualAllocOrig) - (LPVOID aAddress, SIZE_T aSize, DWORD aAllocationType, DWORD aProtect); +void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize, + DWORD aAllocationType, DWORD aProtect); -void* (WINAPI* sMapViewOfFileOrig) - (HANDLE aFileMappingObject, DWORD aDesiredAccess, - DWORD aFileOffsetHigh, DWORD aFileOffsetLow, - SIZE_T aNumBytesToMap); +void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject, + DWORD aDesiredAccess, DWORD aFileOffsetHigh, + DWORD aFileOffsetLow, SIZE_T aNumBytesToMap); -HBITMAP (WINAPI* sCreateDIBSectionOrig) - (HDC aDC, const BITMAPINFO* aBitmapInfo, - UINT aUsage, VOID** aBits, - HANDLE aSection, DWORD aOffset); +HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo, + UINT aUsage, VOID** aBits, + HANDLE aSection, DWORD aOffset); /** * Fire a memory pressure event if it's been long enough since the last one we * fired. */ bool MaybeScheduleMemoryPressureEvent() {
--- a/xpcom/base/CodeAddressService.h +++ b/xpcom/base/CodeAddressService.h @@ -149,17 +149,18 @@ public: nsCodeAddressDetails details; { DescribeCodeAddressLock::Unlock(); (void)NS_DescribeCodeAddress(const_cast<void*>(aPc), &details); DescribeCodeAddressLock::Lock(); } const char* library = mLibraryStrings.Intern(details.library); - entry.Replace(aPc, details.function, library, details.loffset, details.filename, details.lineno); + entry.Replace(aPc, details.function, library, details.loffset, + details.filename, details.lineno); } else { mNumCacheHits++; } MOZ_ASSERT(entry.mPc == aPc); uintptr_t entryPc = (uintptr_t)(entry.mPc);
new file mode 100644 --- /dev/null +++ b/xpcom/base/CountingAllocatorBase.h @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CountingAllocatorBase_h +#define CountingAllocatorBase_h + +#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" +#include "nsIMemoryReporter.h" + +namespace mozilla { + +// This CRTP class handles several details of wrapping allocators and should +// be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC +// and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory +// reporter for a particular third party library: +// +// class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter> +// { +// ... +// NS_IMETHODIMP +// CollectReports(nsIHandleReportCallback* aHandleReport, +// nsISupports* aData, bool aAnonymize) +// { +// return MOZ_COLLECT_REPORT( +// "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES, +// MemoryAllocated(), +// "A description of what we are reporting." +// } +// }; +// +// ...somewhere later in the code... +// SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc, +// MyMemoryReporter::CountingFree); +template<typename T> +class CountingAllocatorBase +{ +public: + CountingAllocatorBase() + { +#ifdef DEBUG + // There must be only one instance of this class, due to |sAmount| being + // static. + static bool hasRun = false; + MOZ_ASSERT(!hasRun); + hasRun = true; +#endif + } + + static size_t + MemoryAllocated() + { + return sAmount; + } + + static void* + CountingMalloc(size_t size) + { + void* p = malloc(size); + sAmount += MallocSizeOfOnAlloc(p); + return p; + } + + static void* + CountingCalloc(size_t nmemb, size_t size) + { + void* p = calloc(nmemb, size); + sAmount += MallocSizeOfOnAlloc(p); + return p; + } + + static void* + CountingRealloc(void* p, size_t size) + { + size_t oldsize = MallocSizeOfOnFree(p); + void *pnew = realloc(p, size); + if (pnew) { + size_t newsize = MallocSizeOfOnAlloc(pnew); + sAmount += newsize - oldsize; + } else if (size == 0) { + // We asked for a 0-sized (re)allocation of some existing pointer + // and received NULL in return. 0-sized allocations are permitted + // to either return NULL or to allocate a unique object per call (!). + // For a malloc implementation that chooses the second strategy, + // that allocation may fail (unlikely, but possible). + // + // Given a NULL return value and an allocation size of 0, then, we + // don't know if that means the original pointer was freed or if + // the allocation of the unique object failed. If the original + // pointer was freed, then we have nothing to do here. If the + // allocation of the unique object failed, the original pointer is + // still valid and we ought to undo the decrement from above. + // However, we have no way of knowing how the underlying realloc + // implementation is behaving. Assuming that the original pointer + // was freed is the safest course of action. We do, however, need + // to note that we freed memory. + sAmount -= oldsize; + } else { + // realloc failed. The amount allocated hasn't changed. + } + return pnew; + } + + // Some library code expects that realloc(x, 0) will free x, which is not + // the behavior of the version of jemalloc we're using, so this wrapped + // version of realloc is needed. + static void* + CountingFreeingRealloc(void* p, size_t size) + { + if (size == 0) { + CountingFree(p); + return nullptr; + } + return CountingRealloc(p, size); + } + + static void + CountingFree(void* p) + { + sAmount -= MallocSizeOfOnFree(p); + free(p); + } + +private: + // |sAmount| can be (implicitly) accessed by multiple threads, so it + // must be thread-safe. + static Atomic<size_t> sAmount; + + MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc) + MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree) +}; + +} // namespace mozilla + +#endif // CountingAllocatorBase_h
--- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -152,17 +152,18 @@ TraceWeakMappingChild(JSTracer* aTrc, vo return; } if (!xpc_IsGrayGCThing(thing) && !tracer->mCb.WantAllTraces()) { return; } if (AddToCCKind(aKind)) { - tracer->mCb.NoteWeakMapping(tracer->mMap, tracer->mKey, tracer->mKeyDelegate, thing); + tracer->mCb.NoteWeakMapping(tracer->mMap, tracer->mKey, + tracer->mKeyDelegate, thing); tracer->mTracedAny = true; } else { JS_TraceChildren(aTrc, thing, aKind); } } struct NoteWeakMapsTracer : public js::WeakMapTracer { @@ -300,17 +301,18 @@ struct Closure { } bool mCycleCollectionEnabled; nsCycleCollectionNoteRootCallback* mCb; }; static void -CheckParticipatesInCycleCollection(void* aThing, const char* aName, void* aClosure) +CheckParticipatesInCycleCollection(void* aThing, const char* aName, + void* aClosure) { Closure* closure = static_cast<Closure*>(aClosure); if (closure->mCycleCollectionEnabled) { return; } if (AddToCCKind(js::GCThingTraceKind(aThing)) && @@ -341,35 +343,35 @@ NoteJSHolder(void* aHolder, nsScriptObje return PL_DHASH_NEXT; } NS_IMETHODIMP JSGCThingParticipant::Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb) { - CycleCollectedJSRuntime* runtime = reinterpret_cast<CycleCollectedJSRuntime*> - (reinterpret_cast<char*>(this) - - offsetof(CycleCollectedJSRuntime, mGCThingCycleCollectorGlobal)); + auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>( + reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime, + mGCThingCycleCollectorGlobal)); runtime->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL, aPtr, js::GCThingTraceKind(aPtr), aCb); return NS_OK; } // NB: This is only used to initialize the participant in // CycleCollectedJSRuntime. It should never be used directly. static JSGCThingParticipant sGCThingCycleCollectorGlobal; NS_IMETHODIMP JSZoneParticipant::Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb) { - CycleCollectedJSRuntime* runtime = reinterpret_cast<CycleCollectedJSRuntime*> - (reinterpret_cast<char*>(this) - - offsetof(CycleCollectedJSRuntime, mJSZoneCycleCollectorGlobal)); + auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>( + reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime, + mJSZoneCycleCollectorGlobal)); MOZ_ASSERT(!aCb.WantAllTraces()); JS::Zone* zone = static_cast<JS::Zone*>(aPtr); runtime->TraverseZone(zone, aCb); return NS_OK; } @@ -484,17 +486,18 @@ CycleCollectedJSRuntime::CycleCollectedJ } if (!JS_AddExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this)) { MOZ_CRASH(); } JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this); JS_SetGCCallback(mJSRuntime, GCCallback, this); JS::SetOutOfMemoryCallback(mJSRuntime, OutOfMemoryCallback, this); - JS::SetLargeAllocationFailureCallback(mJSRuntime, LargeAllocationFailureCallback, this); + JS::SetLargeAllocationFailureCallback(mJSRuntime, + LargeAllocationFailureCallback, this); JS_SetContextCallback(mJSRuntime, ContextCallback, this); JS_SetDestroyZoneCallback(mJSRuntime, XPCStringConvert::FreeZoneCache); JS_SetSweepZoneCallback(mJSRuntime, XPCStringConvert::ClearZoneCache); static js::DOMCallbacks DOMcallbacks = { InstanceClassHasProtoAtDepth }; SetDOMCallbacks(mJSRuntime, &DOMcallbacks); @@ -607,17 +610,18 @@ CycleCollectedJSRuntime::NoteGCThingJSCh nsCycleCollectionTraversalCallback& aCb) const { MOZ_ASSERT(mJSRuntime); TraversalTracer trc(mJSRuntime, aCb); JS_TraceChildren(&trc, aThing, aTraceKind); } void -CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj, +CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp, + JSObject* aObj, nsCycleCollectionTraversalCallback& aCb) const { MOZ_ASSERT(aClasp); MOZ_ASSERT(aClasp == js::GetObjectClass(aObj)); if (NoteCustomGCThingXPCOMChildren(aClasp, aObj, aCb)) { // Nothing else to do! return; @@ -760,17 +764,17 @@ CycleCollectedJSRuntime::GCCallback(JSRu CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData); MOZ_ASSERT(aRuntime == self->Runtime()); self->OnGC(aStatus); } /* static */ void -CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext *aContext, +CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext* aContext, void* aData) { CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData); MOZ_ASSERT(JS_GetRuntime(aContext) == self->Runtime()); self->OnOutOfMemory(); } @@ -1096,17 +1100,18 @@ IncrementalFinalizeRunnable::DeferredFin IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt, nsTArray<nsISupports*>& aSupports, DeferredFinalizerTable& aFinalizers) : mRuntime(aRt) , mFinalizeFunctionToRun(0) { this->mSupports.SwapElements(aSupports); - DeferredFinalizeFunctionHolder* function = mDeferredFinalizeFunctions.AppendElement(); + DeferredFinalizeFunctionHolder* function = + mDeferredFinalizeFunctions.AppendElement(); function->run = ReleaseSliceNow; function->data = &this->mSupports; // Enumerate the hashtable into our array. aFinalizers.Enumerate(DeferredFinalizerEnumerator, &mDeferredFinalizeFunctions); } IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable() @@ -1196,18 +1201,19 @@ CycleCollectedJSRuntime::FinalizeDeferre if (aType == FinalizeIncrementally) { NS_DispatchToCurrentThread(mFinalizeRunnable); } else { mFinalizeRunnable->ReleaseNow(false); MOZ_ASSERT(!mFinalizeRunnable); } } -void -CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState *aStatePtr, OOMState aNewState) +void +CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState* aStatePtr, + OOMState aNewState) { *aStatePtr = aNewState; #ifdef MOZ_CRASHREPORTER CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState ? NS_LITERAL_CSTRING("JSOutOfMemory") : NS_LITERAL_CSTRING("JSLargeAllocationFailure"), aNewState == OOMState::Reporting ? NS_LITERAL_CSTRING("Reporting")
--- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -116,32 +116,23 @@ protected: CycleCollectedJSRuntime(JSRuntime* aParentRuntime, uint32_t aMaxBytes, uint32_t aMaxNurseryBytes); virtual ~CycleCollectedJSRuntime(); size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; void UnmarkSkippableJSHolders(); - virtual void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) - { - } - virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) - { - } + virtual void + TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {} + virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {} - virtual void CustomGCCallback(JSGCStatus aStatus) - { - } - virtual void CustomOutOfMemoryCallback() - { - } - virtual void CustomLargeAllocationFailureCallback() - { - } + virtual void CustomGCCallback(JSGCStatus aStatus) {} + virtual void CustomOutOfMemoryCallback() {} + virtual void CustomLargeAllocationFailureCallback() {} virtual bool CustomContextCallback(JSContext* aCx, unsigned aOperation) { return true; // Don't block context creation. } private: void @@ -186,18 +177,18 @@ private: static void TraverseObjectShim(void* aData, void* aThing); void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb); static void TraceBlackJS(JSTracer* aTracer, void* aData); static void TraceGrayJS(JSTracer* aTracer, void* aData); static void GCCallback(JSRuntime* aRuntime, JSGCStatus aStatus, void* aData); - static void OutOfMemoryCallback(JSContext *aContext, void *aData); - static void LargeAllocationFailureCallback(void *aData); + static void OutOfMemoryCallback(JSContext* aContext, void* aData); + static void LargeAllocationFailureCallback(void* aData); static bool ContextCallback(JSContext* aCx, unsigned aOperation, void* aData); virtual void TraceNativeBlackRoots(JSTracer* aTracer) { }; void TraceNativeGrayRoots(JSTracer* aTracer); enum DeferredFinalizeType { FinalizeIncrementally, @@ -235,17 +226,17 @@ public: // The condition has happened, but a GC cycle ended since then. // // GC is taken as a proxy for "we've been banging on the heap a good bit // now and haven't crashed; the OOM was probably handled correctly". Recovered MOZ_END_NESTED_ENUM_CLASS(OOMState) private: - void AnnotateAndSetOutOfMemory(OOMState *aStatePtr, OOMState aNewState); + void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState); void OnGC(JSGCStatus aStatus); void OnOutOfMemory(); void OnLargeAllocationFailure(); public: void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer); void RemoveJSHolder(void* aHolder); #ifdef DEBUG
--- a/xpcom/base/StaticPtr.h +++ b/xpcom/base/StaticPtr.h @@ -49,36 +49,27 @@ public: #endif StaticAutoPtr<T>& operator=(T* aRhs) { Assign(aRhs); return *this; } - T* get() const - { - return mRawPtr; - } + T* get() const { return mRawPtr; } - operator T*() const - { - return get(); - } + operator T*() const { return get(); } T* operator->() const { MOZ_ASSERT(mRawPtr); return get(); } - T& operator*() const - { - return *get(); - } + T& operator*() const { return *get(); } private: // Disallow copy constructor, but only in debug mode. We only define // a default constructor in debug mode (see above); if we declared // this constructor always, the compiler wouldn't generate a trivial // default constructor for us in non-debug mode. #ifdef DEBUG StaticAutoPtr(StaticAutoPtr<T>& aOther); @@ -115,36 +106,27 @@ public: return *this; } StaticRefPtr<T>& operator=(const StaticRefPtr<T>& aRhs) { return (this = aRhs.mRawPtr); } - T* get() const - { - return mRawPtr; - } + T* get() const { return mRawPtr; } - operator T*() const - { - return get(); - } + operator T*() const { return get(); } T* operator->() const { MOZ_ASSERT(mRawPtr); return get(); } - T& operator*() const - { - return *get(); - } + T& operator*() const { return *get(); } private: void AssignWithAddref(T* aNewPtr) { if (aNewPtr) { aNewPtr->AddRef(); } AssignAssumingAddRef(aNewPtr);
--- a/xpcom/base/SystemMemoryReporter.cpp +++ b/xpcom/base/SystemMemoryReporter.cpp @@ -158,18 +158,19 @@ public: #define REPORT(_path, _amount, _desc) \ REPORT_WITH_CLEANUP(_path, UNITS_BYTES, _amount, _desc, (void)0) NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { // There is lots of privacy-sensitive data in /proc. Just skip this // reporter entirely when anonymization is required. - if (aAnonymize) + if (aAnonymize) { return NS_OK; + } if (!Preferences::GetBool("memory.system_memory_reporter")) { return NS_OK; } // Read relevant fields from /proc/meminfo. int64_t memTotal = 0, memFree = 0; nsresult rv = ReadMemInfo(&memTotal, &memFree); @@ -208,40 +209,41 @@ public: return rv; } private: // These are the cross-cutting measurements across all processes. class ProcessSizes { public: - void Add(const nsACString &aKey, size_t aSize) + void Add(const nsACString& aKey, size_t aSize) { mTagged.Put(aKey, mTagged.Get(aKey) + aSize); } - void Report(nsIHandleReportCallback *aHandleReport, nsISupports *aData) + void Report(nsIHandleReportCallback* aHandleReport, nsISupports* aData) { EnumArgs env = { aHandleReport, aData }; mTagged.EnumerateRead(ReportSizes, &env); } private: nsDataHashtable<nsCStringHashKey, size_t> mTagged; - struct EnumArgs { + struct EnumArgs + { nsIHandleReportCallback* mHandleReport; nsISupports* mData; }; static PLDHashOperator ReportSizes(nsCStringHashKey::KeyType aKey, size_t aAmount, - void *aUserArg) + void* aUserArg) { - const EnumArgs *envp = reinterpret_cast<const EnumArgs*>(aUserArg); + const EnumArgs* envp = reinterpret_cast<const EnumArgs*>(aUserArg); nsAutoCString path("processes/"); path.Append(aKey); nsAutoCString desc("This is the sum of all processes' '"); desc.Append(aKey); desc.AppendLiteral("' numbers."); @@ -330,18 +332,18 @@ private: &processSizes, aTotalPss); fclose(f); if (NS_FAILED(rv)) { continue; } // Report the open file descriptors for this process. nsPrintfCString procFdPath("/proc/%s/fd", pidStr); - rv = CollectOpenFileReports( - aHandleReport, aData, procFdPath, processName); + rv = CollectOpenFileReports(aHandleReport, aData, procFdPath, + processName); if (NS_FAILED(rv)) { break; } } } closedir(d); // Report the "processes/" tree. @@ -942,22 +944,22 @@ private: } else { category = ""; descriptionPrefix = "An uncategorized"; } #undef CHECK_PREFIX const nsCString processName(aProcessName); - nsPrintfCString entryPath( - "open-fds/%s/%s%s/%s", processName.get(), category, linkPath, fd); - nsPrintfCString entryDescription( - "%s file descriptor opened by the process", descriptionPrefix); - REPORT_WITH_CLEANUP( - entryPath, UNITS_COUNT, 1, entryDescription, closedir(d)); + nsPrintfCString entryPath("open-fds/%s/%s%s/%s", + processName.get(), category, linkPath, fd); + nsPrintfCString entryDescription("%s file descriptor opened by the process", + descriptionPrefix); + REPORT_WITH_CLEANUP(entryPath, UNITS_COUNT, 1, entryDescription, + closedir(d)); } } closedir(d); return NS_OK; } nsresult @@ -968,18 +970,18 @@ private: // /sys/kernel/debug/kgsl/proc/<pid>/mem. This file format includes a // header and then entries with types as follows: // gpuaddr useraddr size id flags type usage sglen // hexaddr hexaddr int int str str str int // We care primarily about the usage and size. // For simplicity numbers will be uint64_t, strings 63 chars max. const char* const kScanFormat = - "%" SCNx64 " %" SCNx64 " %" SCNu64 " %" SCNu64 - " %63s %63s %63s %" SCNu64; + "%" SCNx64 " %" SCNx64 " %" SCNu64 " %" SCNu64 + " %63s %63s %63s %" SCNu64; const int kNumFields = 8; const size_t kStringSize = 64; DIR* d = opendir("/sys/kernel/debug/kgsl/proc/"); if (!d) { if (NS_WARN_IF(errno != ENOENT && errno != EACCES)) { return NS_ERROR_FAILURE; }
--- a/xpcom/base/VisualEventTracer.cpp +++ b/xpcom/base/VisualEventTracer.cpp @@ -434,17 +434,18 @@ void Init() { #ifdef MOZ_VISUAL_EVENT_TRACER const char* logEvents = PR_GetEnv("MOZ_PROFILING_EVENTS"); if (logEvents && *logEvents) { gEventFilter = EventFilter::Build(logEvents); } - PRStatus status = PR_NewThreadPrivateIndex(&gThreadPrivateIndex, &RecordBatch::Close); + PRStatus status = PR_NewThreadPrivateIndex(&gThreadPrivateIndex, + &RecordBatch::Close); if (status != PR_SUCCESS) { return; } gMonitor = new mozilla::Monitor("Profiler"); if (!gMonitor) { return; } @@ -492,18 +493,18 @@ Mark(uint32_t aType, void* aItem, const if (aType == eNone) { return; } if (!CheckEventFilters(aType, aItem, aText)) { // Events use just aText return; } - RecordBatch* threadLogPrivate = static_cast<RecordBatch*>( - PR_GetThreadPrivate(gThreadPrivateIndex)); + RecordBatch* threadLogPrivate = + static_cast<RecordBatch*>(PR_GetThreadPrivate(gThreadPrivateIndex)); if (!threadLogPrivate) { threadLogPrivate = RecordBatch::Register(); if (!threadLogPrivate) { return; } PR_SetThreadPrivate(gThreadPrivateIndex, threadLogPrivate); }
--- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -67,16 +67,17 @@ if CONFIG['OS_ARCH'] == 'WINNT': ] if CONFIG['MOZ_DEBUG']: EXPORTS += ['pure.h'] SOURCES += ['pure_api.c'] EXPORTS.mozilla += [ 'AvailableMemoryTracker.h', 'ClearOnShutdown.h', + 'CountingAllocatorBase.h', 'CycleCollectedJSRuntime.h', 'Debug.h', 'LinuxUtils.h', 'nsMemoryInfoDumper.h', 'StackWalk.h', 'StaticMutex.h', 'StaticPtr.h', 'SystemMemoryReporter.h',
--- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -29,17 +29,19 @@ #ifdef XP_WIN #include <windows.h> #endif using namespace mozilla; NS_IMPL_ADDREF(nsConsoleService) NS_IMPL_RELEASE(nsConsoleService) -NS_IMPL_CLASSINFO(nsConsoleService, nullptr, nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, NS_CONSOLESERVICE_CID) +NS_IMPL_CLASSINFO(nsConsoleService, nullptr, + nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, + NS_CONSOLESERVICE_CID) NS_IMPL_QUERY_INTERFACE_CI(nsConsoleService, nsIConsoleService) NS_IMPL_CI_INTERFACE_GETTER(nsConsoleService, nsIConsoleService) static bool sLoggingEnabled = true; static bool sLoggingBuffered = true; nsConsoleService::nsConsoleService() : mMessages(nullptr) @@ -52,17 +54,17 @@ nsConsoleService::nsConsoleService() // hm, but worry about circularity, bc we want to be able to report // prefs errs... mBufferSize = 250; } nsConsoleService::~nsConsoleService() { uint32_t i = 0; - while (i < mBufferSize && mMessages[i] != nullptr) { + while (i < mBufferSize && mMessages[i]) { NS_RELEASE(mMessages[i]); i++; } if (mMessages) { nsMemory::Free(mMessages); } } @@ -261,17 +263,18 @@ nsConsoleService::LogStringMessage(const return NS_OK; } nsRefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage)); return this->LogMessage(msg); } NS_IMETHODIMP -nsConsoleService::GetMessageArray(uint32_t* aCount, nsIConsoleMessage*** aMessages) +nsConsoleService::GetMessageArray(uint32_t* aCount, + nsIConsoleMessage*** aMessages) { nsIConsoleMessage** messageArray; /* * Lock the whole method, as we don't want anyone mucking with mCurrent or * mFull while we're copying out the buffer. */ MutexAutoLock lock(mLock);
--- a/xpcom/base/nsConsoleService.h +++ b/xpcom/base/nsConsoleService.h @@ -48,18 +48,20 @@ public: enum OutputMode { SuppressLog, OutputToLog }; virtual nsresult LogMessageWithMode(nsIConsoleMessage* aMessage, OutputMode aOutputMode); - typedef nsInterfaceHashtable<nsISupportsHashKey, nsIConsoleListener> ListenerHash; - void EnumerateListeners(ListenerHash::EnumReadFunction aFunction, void* aClosure); + typedef nsInterfaceHashtable<nsISupportsHashKey, + nsIConsoleListener> ListenerHash; + void EnumerateListeners(ListenerHash::EnumReadFunction aFunction, + void* aClosure); private: ~nsConsoleService(); // Circular buffer of saved messages nsIConsoleMessage** mMessages; // How big?
--- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -378,17 +378,18 @@ public: { return !mSentinelAndBlocks[0].block && !mSentinelAndBlocks[1].block; } #endif private: struct Block; - union PtrInfoOrBlock { + union PtrInfoOrBlock + { // Use a union to avoid reinterpret_cast and the ensuing // potential aliasing bugs. PtrInfo* ptrInfo; Block* block; }; struct Block { enum { BlockSize = 16 * 1024 }; @@ -425,25 +426,19 @@ private: { return mSentinelAndBlocks[1].block; } public: class Iterator { public: - Iterator() : mPointer(nullptr) - { - } - explicit Iterator(PtrInfoOrBlock* aPointer) : mPointer(aPointer) - { - } - Iterator(const Iterator& aOther) : mPointer(aOther.mPointer) - { - } + Iterator() : mPointer(nullptr) {} + explicit Iterator(PtrInfoOrBlock* aPointer) : mPointer(aPointer) {} + Iterator(const Iterator& aOther) : mPointer(aOther.mPointer) {} Iterator& operator++() { if (!mPointer->ptrInfo) { // Null pointer is a sentinel for link to the next block. mPointer = (mPointer + 1)->block->mPointers; } ++mPointer; @@ -958,17 +953,18 @@ CanonicalizeParticipant(void** aParti, n ToParticipant(nsparti, &xcp); *aParti = nsparti; *aCp = xcp; } } struct nsPurpleBufferEntry { - union { + union + { void* mObject; // when low bit unset nsPurpleBufferEntry* mNextInFreeList; // when low bit set }; nsCycleCollectingAutoRefCnt* mRefCnt; nsCycleCollectionParticipant* mParticipant; // nullptr for nsISupports }; @@ -993,17 +989,17 @@ private: // Ensure Block is the right size (see above). static_assert( sizeof(Block) == 16384 || // 32-bit sizeof(Block) == 32768, // 64-bit "ill-sized nsPurpleBuffer::Block" ); } - template <class PurpleVisitor> + template<class PurpleVisitor> void VisitEntries(nsPurpleBuffer& aBuffer, PurpleVisitor& aVisitor) { nsPurpleBufferEntry* eEnd = ArrayEnd(mEntries); for (nsPurpleBufferEntry* e = mEntries; e != eEnd; ++e) { if (!(uintptr_t(e->mObject) & uintptr_t(1))) { aVisitor.Visit(aBuffer, e); } } @@ -1022,17 +1018,17 @@ public: InitBlocks(); } ~nsPurpleBuffer() { FreeBlocks(); } - template <class PurpleVisitor> + template<class PurpleVisitor> void VisitEntries(PurpleVisitor& aVisitor) { for (Block* b = &mFirstBlock; b; b = b->mNext) { b->VisitEntries(*this, aVisitor); } } void InitBlocks() @@ -1213,24 +1209,26 @@ nsPurpleBuffer::SelectPointers(CCGraphBu NS_ASSERTION(mCount == 0, "AddPurpleRoot failed"); if (mCount == 0) { FreeBlocks(); InitBlocks(); } } -enum ccPhase { +enum ccPhase +{ IdlePhase, GraphBuildingPhase, ScanAndCollectWhitePhase, CleanupPhase }; -enum ccType { +enum ccType +{ SliceCC, /* If a CC is in progress, continue it. Otherwise, start a new one. */ ManualCC, /* Explicitly triggered. */ ShutdownCC /* Shutdown CC, used for finding leaks. */ }; #ifdef MOZ_NUWA_PROCESS #include "ipc/Nuwa.h" #endif @@ -1350,17 +1348,17 @@ NS_IMPL_ISUPPORTS(nsCycleCollector, nsIM /** * GraphWalker is templatized over a Visitor class that must provide * the following two methods: * * bool ShouldVisitNode(PtrInfo const *pi); * void VisitNode(PtrInfo *pi); */ -template <class Visitor> +template<class Visitor> class GraphWalker { private: Visitor mVisitor; void DoWalk(nsDeque& aQueue); void CheckedPush(nsDeque& aQueue, PtrInfo* aPi) @@ -1423,38 +1421,38 @@ ToParticipant(nsISupports* aPtr, nsXPCOM { // We use QI to move from an nsISupports to an // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper // object that implements traversal and unlinking logic for the nsISupports // in question. CallQueryInterface(aPtr, aCp); } -template <class Visitor> +template<class Visitor> MOZ_NEVER_INLINE void GraphWalker<Visitor>::Walk(PtrInfo* aPi) { nsDeque queue; CheckedPush(queue, aPi); DoWalk(queue); } -template <class Visitor> +template<class Visitor> MOZ_NEVER_INLINE void GraphWalker<Visitor>::WalkFromRoots(CCGraph& aGraph) { nsDeque queue; NodePool::Enumerator etor(aGraph.mNodes); for (uint32_t i = 0; i < aGraph.mRootCount; ++i) { CheckedPush(queue, etor.GetNext()); } DoWalk(queue); } -template <class Visitor> +template<class Visitor> MOZ_NEVER_INLINE void GraphWalker<Visitor>::DoWalk(nsDeque& aQueue) { // Use a aQueue to match the breadth-first traversal used when we // built the graph, for hopefully-better locality. while (aQueue.GetSize() > 0) { PtrInfo* pi = static_cast<PtrInfo*>(aQueue.PopFront()); @@ -1471,17 +1469,18 @@ GraphWalker<Visitor>::DoWalk(nsDeque& aQ struct CCGraphDescriber : public LinkedListElement<CCGraphDescriber> { CCGraphDescriber() : mAddress("0x"), mCnt(0), mType(eUnknown) { } - enum Type { + enum Type + { eRefCountedObject, eGCedObject, eGCMarkedObject, eEdge, eRoot, eGarbage, eUnknown }; @@ -1585,17 +1584,18 @@ private: fclose(mGCLog.mStream); } if (mCCLog.mStream) { MozillaUnRegisterDebugFILE(mCCLog.mStream); fclose(mCCLog.mStream); } } - struct FileInfo { + struct FileInfo + { const char* const mPrefix; nsCOMPtr<nsIFile> mFile; FILE* mStream; explicit FileInfo(const char* aPrefix) : mPrefix(aPrefix), mStream(nullptr) { } }; /** @@ -1622,19 +1622,17 @@ private: NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, &logFile); } // On Android or B2G, this function will open a file named // aFilename under a memory-reporting-specific folder // (/data/local/tmp/memory-reports). Otherwise, it will open a // file named aFilename under "NS_OS_TEMP_DIR". - nsresult rv = nsDumpUtils::OpenTempFile( - filename, - &logFile, + nsresult rv = nsDumpUtils::OpenTempFile(filename, &logFile, NS_LITERAL_CSTRING("memory-reports")); if (NS_FAILED(rv)) { NS_IF_RELEASE(logFile); return nullptr; } return dont_AddRef(logFile); } @@ -1646,62 +1644,66 @@ private: // completes. (We do this because we don't want scripts which poll // the filesystem looking for GC/CC dumps to grab a file before we're // finished writing to it.) nsAutoCString incomplete; incomplete += "incomplete-"; incomplete += aLog->mPrefix; MOZ_ASSERT(!aLog->mFile); aLog->mFile = CreateTempFile(incomplete.get()); - if (NS_WARN_IF(!aLog->mFile)) + if (NS_WARN_IF(!aLog->mFile)) { return NS_ERROR_UNEXPECTED; + } MOZ_ASSERT(!aLog->mStream); aLog->mFile->OpenANSIFileDesc("w", &aLog->mStream); - if (NS_WARN_IF(!aLog->mStream)) + if (NS_WARN_IF(!aLog->mStream)) { return NS_ERROR_UNEXPECTED; + } MozillaRegisterDebugFILE(aLog->mStream); return NS_OK; } nsresult CloseLog(FileInfo* aLog, const nsAString& aCollectorKind) { MOZ_ASSERT(aLog->mStream); MOZ_ASSERT(aLog->mFile); MozillaUnRegisterDebugFILE(aLog->mStream); fclose(aLog->mStream); aLog->mStream = nullptr; // Strip off "incomplete-". nsCOMPtr<nsIFile> logFileFinalDestination = CreateTempFile(aLog->mPrefix); - if (NS_WARN_IF(!logFileFinalDestination)) + if (NS_WARN_IF(!logFileFinalDestination)) { return NS_ERROR_UNEXPECTED; + } nsAutoString logFileFinalDestinationName; logFileFinalDestination->GetLeafName(logFileFinalDestinationName); - if (NS_WARN_IF(logFileFinalDestinationName.IsEmpty())) + if (NS_WARN_IF(logFileFinalDestinationName.IsEmpty())) { return NS_ERROR_UNEXPECTED; + } aLog->mFile->MoveTo(/* directory */ nullptr, logFileFinalDestinationName); // Save the file path. aLog->mFile = logFileFinalDestination; // Log to the error console. nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (cs) { // Copy out the path. nsAutoString logPath; logFileFinalDestination->GetPath(logPath); - nsString msg = aCollectorKind - + NS_LITERAL_STRING(" Collector log dumped to ") + logPath; + nsString msg = aCollectorKind + + NS_LITERAL_STRING(" Collector log dumped to ") + logPath; cs->LogStringMessage(msg.get()); } return NS_OK; } int32_t mProcessIdentifier; nsString mFilenameIdentifier; FileInfo mGCLog; @@ -1839,17 +1841,17 @@ public: aMarked ? ".marked" : "", aObjectDescription); } if (mWantAfterProcessing) { CCGraphDescriber* d = new CCGraphDescriber(); mDescribers.insertBack(d); mCurrentAddress.AssignLiteral("0x"); mCurrentAddress.AppendInt(aAddress, 16); d->mType = aMarked ? CCGraphDescriber::eGCMarkedObject : - CCGraphDescriber::eGCedObject; + CCGraphDescriber::eGCedObject; d->mAddress = mCurrentAddress; d->mName.Append(aObjectDescription); if (aCompartmentAddress) { d->mCompartmentOrToAddress.AssignLiteral("0x"); d->mCompartmentOrToAddress.AppendInt(aCompartmentAddress, 16); } else { d->mCompartmentOrToAddress.SetIsVoid(true); } @@ -2351,17 +2353,18 @@ CCGraphBuilder::AddWeakMapNode(void* aNo if (JS::Zone* zone = MergeZone(aNode)) { return AddNode(zone, mJSZoneParticipant); } return AddNode(aNode, mJSParticipant); } NS_IMETHODIMP_(void) -CCGraphBuilder::NoteWeakMapping(void* aMap, void* aKey, void* aKdelegate, void* aVal) +CCGraphBuilder::NoteWeakMapping(void* aMap, void* aKey, void* aKdelegate, + void* aVal) { // Don't try to optimize away the entry here, as we've already attempted to // do that in TraceWeakMapping in nsXPConnect. WeakMapping* mapping = mGraph.mWeakMaps.AppendElement(); mapping->mMap = aMap ? AddWeakMapNode(aMap) : nullptr; mapping->mKey = aKey ? AddWeakMapNode(aKey) : nullptr; mapping->mKeyDelegate = aKdelegate ? AddWeakMapNode(aKdelegate) : mapping->mKey; mapping->mVal = aVal ? AddWeakMapNode(aVal) : nullptr; @@ -2645,28 +2648,28 @@ public: virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName, void* aClosure) const { JS::Value val = *aValue; if (val.isMarkable()) { void* thing = val.toGCThing(); if (thing && xpc_GCThingIsGrayCCThing(thing)) { - MOZ_ASSERT(!js::gc::IsInsideNursery((js::gc::Cell *)thing)); + MOZ_ASSERT(!js::gc::IsInsideNursery((js::gc::Cell*)thing)); mCollector->GetJSPurpleBuffer()->mValues.AppendElement(val); } } } virtual void Trace(JS::Heap<jsid>* aId, const char* aName, void* aClosure) const { } - void AppendJSObjectToPurpleBuffer(JSObject *obj) const + void AppendJSObjectToPurpleBuffer(JSObject* obj) const { if (obj && xpc_GCThingIsGrayCCThing(obj)) { MOZ_ASSERT(!js::gc::IsInsideNursery(JS::AsCell(obj))); mCollector->GetJSPurpleBuffer()->mObjects.AppendElement(obj); } } virtual void Trace(JS::Heap<JSObject*>* aObject, const char* aName, @@ -2897,19 +2900,20 @@ struct ScanBlackVisitor private: uint32_t& mWhiteNodeCount; bool& mFailed; }; static void FloodBlackNode(uint32_t& aWhiteNodeCount, bool& aFailed, PtrInfo* aPi) { - GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(aWhiteNodeCount, aFailed)).Walk(aPi); - MOZ_ASSERT(aPi->mColor == black || !aPi->WasTraversed(), - "FloodBlackNode should make aPi black"); + GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(aWhiteNodeCount, + aFailed)).Walk(aPi); + MOZ_ASSERT(aPi->mColor == black || !aPi->WasTraversed(), + "FloodBlackNode should make aPi black"); } // Iterate over the WeakMaps. If we mark anything while iterating // over the WeakMaps, we must iterate over all of the WeakMaps again. void nsCycleCollector::ScanWeakMaps() { bool anyChanged; @@ -3006,23 +3010,26 @@ nsCycleCollector::ScanIncrementalRoots() // refcounted object is purple, it may have been AddRef'd during the current // ICC. (It may also have only been released.) If that is the case, we cannot // be sure that the set of things pointing to the object in the CC graph // is accurate. Therefore, for safety, we treat any purple objects as being // live during the current CC. We don't remove anything from the purple // buffer here, so these objects will be suspected and freed in the next CC // if they are garbage. bool failed = false; - PurpleScanBlackVisitor purpleScanBlackVisitor(mGraph, mListener, mWhiteNodeCount, failed); + PurpleScanBlackVisitor purpleScanBlackVisitor(mGraph, mListener, + mWhiteNodeCount, failed); mPurpleBuf.VisitEntries(purpleScanBlackVisitor); timeLog.Checkpoint("ScanIncrementalRoots::fix purple"); bool hasJSRuntime = !!mJSRuntime; - nsCycleCollectionParticipant* jsParticipant = hasJSRuntime ? mJSRuntime->GCThingParticipant() : nullptr; - nsCycleCollectionParticipant* zoneParticipant = hasJSRuntime ? mJSRuntime->ZoneParticipant() : nullptr; + nsCycleCollectionParticipant* jsParticipant = + hasJSRuntime ? mJSRuntime->GCThingParticipant() : nullptr; + nsCycleCollectionParticipant* zoneParticipant = + hasJSRuntime ? mJSRuntime->ZoneParticipant() : nullptr; bool hasListener = !!mListener; NodePool::Enumerator etor(mGraph.mNodes); while (!etor.IsDone()) { PtrInfo* pi = etor.GetNext(); // As an optimization, if an object has already been determined to be live, // don't consider it further. We can't do this if there is a listener, @@ -3258,29 +3265,31 @@ nsCycleCollector::CollectWhite() if (mBeforeUnlinkCB) { mBeforeUnlinkCB(); timeLog.Checkpoint("CollectWhite::BeforeUnlinkCB"); } for (uint32_t i = 0; i < count; ++i) { PtrInfo* pinfo = whiteNodes.ElementAt(i); - MOZ_ASSERT(pinfo->mParticipant, "Unlink shouldn't see objects removed from graph."); + MOZ_ASSERT(pinfo->mParticipant, + "Unlink shouldn't see objects removed from graph."); pinfo->mParticipant->Unlink(pinfo->mPointer); #ifdef DEBUG if (mJSRuntime) { mJSRuntime->AssertNoObjectsToTrace(pinfo->mPointer); } #endif } timeLog.Checkpoint("CollectWhite::Unlink"); for (uint32_t i = 0; i < count; ++i) { PtrInfo* pinfo = whiteNodes.ElementAt(i); - MOZ_ASSERT(pinfo->mParticipant, "Unroot shouldn't see objects removed from graph."); + MOZ_ASSERT(pinfo->mParticipant, + "Unroot shouldn't see objects removed from graph."); pinfo->mParticipant->Unroot(pinfo->mPointer); } timeLog.Checkpoint("CollectWhite::Unroot"); nsCycleCollector_dispatchDeferredDeletion(false); timeLog.Checkpoint("CollectWhite::dispatchDeferredDeletion"); mIncrementalPhase = CleanupPhase; @@ -3471,18 +3480,18 @@ nsCycleCollector::FixGrayBits(bool aForc CC_TELEMETRY(_NEED_GC, needGC); if (!needGC) { return; } mResults.mForcedGC = true; } TimeLog timeLog; - mJSRuntime->GarbageCollect(aForceGC ? JS::gcreason::SHUTDOWN_CC - : JS::gcreason::CC_FORCED); + mJSRuntime->GarbageCollect(aForceGC ? JS::gcreason::SHUTDOWN_CC : + JS::gcreason::CC_FORCED); timeLog.Checkpoint("GC()"); } void nsCycleCollector::CleanupAfterCollection() { TimeLog timeLog; MOZ_ASSERT(mIncrementalPhase == CleanupPhase); @@ -3738,17 +3747,18 @@ nsCycleCollector::BeginCollection(ccType // Set up the data structures for building the graph. mGraph.Init(); mResults.Init(); bool mergeZones = ShouldMergeZones(aCCType); mResults.mMergedZones = mergeZones; MOZ_ASSERT(!mBuilder, "Forgot to clear mBuilder"); - mBuilder = new CCGraphBuilder(mGraph, mResults, mJSRuntime, mListener, mergeZones); + mBuilder = new CCGraphBuilder(mGraph, mResults, mJSRuntime, mListener, + mergeZones); if (mJSRuntime) { mJSRuntime->TraverseRoots(*mBuilder); timeLog.Checkpoint("mJSRuntime->TraverseRoots()"); } AutoRestore<bool> ar(mScanInProgress); MOZ_ASSERT(!mScanInProgress); @@ -4100,17 +4110,17 @@ nsCycleCollector_forgetSkippable(bool aR { CollectorData* data = sCollectorData.get(); // We should have started the cycle collector by now. MOZ_ASSERT(data); MOZ_ASSERT(data->mCollector); PROFILER_LABEL("nsCycleCollector", "forgetSkippable", - js::ProfileEntry::Category::CC); + js::ProfileEntry::Category::CC); TimeLog timeLog; data->mCollector->ForgetSkippable(aRemoveChildlessNodes, aAsyncSnowWhiteFreeing); timeLog.Checkpoint("ForgetSkippable()"); } void @@ -4150,33 +4160,33 @@ nsCycleCollector_collect(nsICycleCollect { CollectorData* data = sCollectorData.get(); // We should have started the cycle collector by now. MOZ_ASSERT(data); MOZ_ASSERT(data->mCollector); PROFILER_LABEL("nsCycleCollector", "collect", - js::ProfileEntry::Category::CC); + js::ProfileEntry::Category::CC); SliceBudget unlimitedBudget; data->mCollector->Collect(ManualCC, unlimitedBudget, aManualListener); } void nsCycleCollector_collectSlice(int64_t aSliceTime) { CollectorData* data = sCollectorData.get(); // We should have started the cycle collector by now. MOZ_ASSERT(data); MOZ_ASSERT(data->mCollector); PROFILER_LABEL("nsCycleCollector", "collectSlice", - js::ProfileEntry::Category::CC); + js::ProfileEntry::Category::CC); SliceBudget budget; if (aSliceTime >= 0) { budget = SliceBudget(SliceBudget::TimeBudget(aSliceTime)); } data->mCollector->Collect(SliceCC, budget, nullptr); } @@ -4185,17 +4195,17 @@ nsCycleCollector_collectSliceWork(int64_ { CollectorData* data = sCollectorData.get(); // We should have started the cycle collector by now. MOZ_ASSERT(data); MOZ_ASSERT(data->mCollector); PROFILER_LABEL("nsCycleCollector", "collectSliceWork", - js::ProfileEntry::Category::CC); + js::ProfileEntry::Category::CC); SliceBudget budget; if (aSliceWork >= 0) { budget = SliceBudget(SliceBudget::WorkBudget(aSliceWork)); } data->mCollector->Collect(SliceCC, budget, nullptr); } @@ -4230,17 +4240,17 @@ nsCycleCollector_finishAnyCurrentCollect void nsCycleCollector_shutdown() { CollectorData* data = sCollectorData.get(); if (data) { MOZ_ASSERT(data->mCollector); PROFILER_LABEL("nsCycleCollector", "shutdown", - js::ProfileEntry::Category::CC); + js::ProfileEntry::Category::CC); data->mCollector->Shutdown(); data->mCollector = nullptr; if (!data->mRuntime) { delete data; sCollectorData.set(nullptr); } }
--- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -231,17 +231,18 @@ static PRLogModuleInfo* gDebugLog; static void InitLog() { if (0 == gDebugLog) { gDebugLog = PR_NewLogModule("nsDebug"); } } -enum nsAssertBehavior { +enum nsAssertBehavior +{ NS_ASSERT_UNINITIALIZED, NS_ASSERT_WARN, NS_ASSERT_SUSPEND, NS_ASSERT_STACK, NS_ASSERT_TRAP, NS_ASSERT_ABORT, NS_ASSERT_STACK_AND_ABORT }; @@ -506,17 +507,18 @@ RealBreak() // Abort() calls this function, don't call it! static void Break(const char* aMsg) { #if defined(_WIN32) static int ignoreDebugger; if (!ignoreDebugger) { const char* shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG"); - ignoreDebugger = 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1")); + ignoreDebugger = + 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1")); } if ((ignoreDebugger == 2) || !::IsDebuggerPresent()) { DWORD code = IDRETRY; /* Create the debug dialog out of process to avoid the crashes caused by * Windows events leaking into our event loop from an in process dialog. * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg). * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792 @@ -582,18 +584,17 @@ static const nsDebugImpl kImpl; nsresult nsDebugImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr) { if (NS_WARN_IF(aOuter)) { return NS_ERROR_NO_AGGREGATION; } - return const_cast<nsDebugImpl*>(&kImpl)-> - QueryInterface(aIID, aInstancePtr); + return const_cast<nsDebugImpl*>(&kImpl)->QueryInterface(aIID, aInstancePtr); } //////////////////////////////////////////////////////////////////////////////// nsresult NS_ErrorAccordingToNSPR() { PRErrorCode err = PR_GetError();
--- a/xpcom/base/nsDumpUtils.cpp +++ b/xpcom/base/nsDumpUtils.cpp @@ -470,18 +470,17 @@ nsDumpUtils::OpenTempFile(const nsACStri (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777); nsAutoCString dirPath; rv = (*aFile)->GetNativePath(dirPath); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) - { + while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) { } } #endif nsCOMPtr<nsIFile> file(*aFile); rv = file->AppendNative(aFilename); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -498,15 +497,14 @@ nsDumpUtils::OpenTempFile(const nsACStri // CreateUnique call above are not sufficient on Android, which runs with a // umask. nsAutoCString path; rv = file->GetNativePath(path); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - while (chmod(path.get(), 0666) == -1 && errno == EINTR) - { + while (chmod(path.get(), 0666) == -1 && errno == EINTR) { } #endif return NS_OK; }
--- a/xpcom/base/nsErrorService.cpp +++ b/xpcom/base/nsErrorService.cpp @@ -6,27 +6,29 @@ #include "nsErrorService.h" #include "nsCRTGlue.h" #include "nsAutoPtr.h" NS_IMPL_ISUPPORTS(nsErrorService, nsIErrorService) nsresult -nsErrorService::Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr) +nsErrorService::Create(nsISupports* aOuter, const nsIID& aIID, + void** aInstancePtr) { if (NS_WARN_IF(aOuter)) { return NS_ERROR_NO_AGGREGATION; } nsRefPtr<nsErrorService> serv = new nsErrorService(); return serv->QueryInterface(aIID, aInstancePtr); } NS_IMETHODIMP -nsErrorService::RegisterErrorStringBundle(int16_t aErrorModule, const char* aStringBundleURL) +nsErrorService::RegisterErrorStringBundle(int16_t aErrorModule, + const char* aStringBundleURL) { mErrorStringBundleURLMap.Put(aErrorModule, new nsCString(aStringBundleURL)); return NS_OK; } NS_IMETHODIMP nsErrorService::UnregisterErrorStringBundle(int16_t aErrorModule) {
--- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -439,17 +439,16 @@ interface nsIMemoryReporterManager : nsI out double jsMilliseconds, out double nonJSMilliseconds); }; %{C++ #include "js/TypeDecls.h" #include "nsStringGlue.h" #include "nsTArray.h" -#include "mozilla/Atomics.h" class nsPIDOMWindow; // nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback // around for backwards compatibility. typedef nsIMemoryReporterCallback nsIHandleReportCallback; namespace mozilla { @@ -569,140 +568,16 @@ void RunReportersForThisProcess(); return moz_malloc_size_of(aPtr); \ } #define MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(fn) \ static size_t fn(const void* aPtr) \ { \ return moz_malloc_size_of(aPtr); \ } -namespace mozilla { - -// This CRTP class handles several details of wrapping allocators and should -// be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC -// and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory -// reporter for a particular third party library: -// -// class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter> -// { -// ... -// NS_IMETHODIMP -// CollectReports(nsIHandleReportCallback* aHandleReport, -// nsISupports* aData, bool aAnonymize) -// { -// return MOZ_COLLECT_REPORT( -// "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES, -// MemoryAllocated(), -// "A description of what we are reporting." -// } -// }; -// -// ...somewhere later in the code... -// SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc, -// MyMemoryReporter::CountingFree); -template<typename T> -class CountingAllocatorBase -{ -public: - CountingAllocatorBase() - { -#ifdef DEBUG - // There must be only one instance of this class, due to |sAmount| being - // static. - static bool hasRun = false; - MOZ_ASSERT(!hasRun); - hasRun = true; -#endif - } - - static size_t - MemoryAllocated() - { - return sAmount; - } - - static void* - CountingMalloc(size_t size) - { - void* p = malloc(size); - sAmount += MallocSizeOfOnAlloc(p); - return p; - } - - static void* - CountingCalloc(size_t nmemb, size_t size) - { - void* p = calloc(nmemb, size); - sAmount += MallocSizeOfOnAlloc(p); - return p; - } - - static void* - CountingRealloc(void* p, size_t size) - { - size_t oldsize = MallocSizeOfOnFree(p); - void *pnew = realloc(p, size); - if (pnew) { - size_t newsize = MallocSizeOfOnAlloc(pnew); - sAmount += newsize - oldsize; - } else if (size == 0) { - // We asked for a 0-sized (re)allocation of some existing pointer - // and received NULL in return. 0-sized allocations are permitted - // to either return NULL or to allocate a unique object per call (!). - // For a malloc implementation that chooses the second strategy, - // that allocation may fail (unlikely, but possible). - // - // Given a NULL return value and an allocation size of 0, then, we - // don't know if that means the original pointer was freed or if - // the allocation of the unique object failed. If the original - // pointer was freed, then we have nothing to do here. If the - // allocation of the unique object failed, the original pointer is - // still valid and we ought to undo the decrement from above. - // However, we have no way of knowing how the underlying realloc - // implementation is behaving. Assuming that the original pointer - // was freed is the safest course of action. We do, however, need - // to note that we freed memory. - sAmount -= oldsize; - } else { - // realloc failed. The amount allocated hasn't changed. - } - return pnew; - } - - // Some library code expects that realloc(x, 0) will free x, which is not - // the behavior of the version of jemalloc we're using, so this wrapped - // version of realloc is needed. - static void* - CountingFreeingRealloc(void* p, size_t size) - { - if (size == 0) { - CountingFree(p); - return nullptr; - } - return CountingRealloc(p, size); - } - - static void - CountingFree(void* p) - { - sAmount -= MallocSizeOfOnFree(p); - free(p); - } - -private: - // |sAmount| can be (implicitly) accessed by multiple threads, so it - // must be thread-safe. - static Atomic<size_t> sAmount; - - MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc) - MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree) -}; - -} - // This macro assumes the presence of appropriate |aHandleReport| and |aData| // variables. #define MOZ_COLLECT_REPORT(path, kind, units, amount, description) \ aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(path), \ kind, units, amount, \ NS_LITERAL_CSTRING(description), aData) %}
--- a/xpcom/base/nsMacUtilsImpl.cpp +++ b/xpcom/base/nsMacUtilsImpl.cpp @@ -32,17 +32,18 @@ nsMacUtilsImpl::GetArchString(nsAString& CFArrayRef archList = ::CFBundleCopyExecutableArchitectures(mainBundle); if (!archList) { return NS_ERROR_FAILURE; } CFIndex archCount = ::CFArrayGetCount(archList); for (CFIndex i = 0; i < archCount; i++) { - CFNumberRef arch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(archList, i)); + CFNumberRef arch = + static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(archList, i)); int archInt = 0; if (!::CFNumberGetValue(arch, kCFNumberIntType, &archInt)) { ::CFRelease(archList); return NS_ERROR_FAILURE; } if (archInt == kCFBundleExecutableArchitecturePPC) {
--- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -119,17 +119,18 @@ public: private: ~GCAndCCLogDumpRunnable() {} const nsString mIdentifier; const bool mDumpAllTraces; const bool mDumpChildProcesses; }; -NS_IMPL_ISUPPORTS_INHERITED(GCAndCCLogDumpRunnable, nsRunnable, nsIDumpGCAndCCLogsCallback) +NS_IMPL_ISUPPORTS_INHERITED(GCAndCCLogDumpRunnable, nsRunnable, + nsIDumpGCAndCCLogsCallback) } // anonymous namespace #if defined(MOZ_SUPPORTS_RT_SIGNALS) // { namespace { /* * The following code supports dumping about:memory upon receiving a signal. @@ -173,34 +174,34 @@ void doMemoryReport(const uint8_t aRecvS NS_DispatchToMainThread(runnable); } void doGCCCDump(const uint8_t aRecvSig) { LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", aRecvSig); // Dump GC and CC logs (from the main thread). nsRefPtr<GCAndCCLogDumpRunnable> runnable = - new GCAndCCLogDumpRunnable( - /* identifier = */ EmptyString(), - /* allTraces = */ true, - /* dumpChildProcesses = */ true); + new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(), + /* allTraces = */ true, + /* dumpChildProcesses = */ true); NS_DispatchToMainThread(runnable); } } // anonymous namespace #endif // MOZ_SUPPORTS_RT_SIGNALS } #if defined(MOZ_SUPPORTS_FIFO) // { namespace { void doMemoryReport(const nsCString& aInputStr) { bool minimize = aInputStr.EqualsLiteral("minimize memory report"); - LOG("FifoWatcher(command:%s) dispatching memory report runnable.", aInputStr.get()); + LOG("FifoWatcher(command:%s) dispatching memory report runnable.", + aInputStr.get()); nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable = new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(), /* anonymize = */ false, minimize); NS_DispatchToMainThread(runnable); } void @@ -309,17 +310,18 @@ EnsureNonEmptyIdentifier(nsAString& aIde // generates and also the files generated by this process's children, allowing // us to identify which files are from the same memory report request. aIdentifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000); } // Use XPCOM refcounting to fire |onFinish| when all reference-holders // (remote dump actors or the |DumpGCAndCCLogsToFile| activation itself) // have gone away. -class nsDumpGCAndCCLogsCallbackHolder MOZ_FINAL : public nsIDumpGCAndCCLogsCallback +class nsDumpGCAndCCLogsCallbackHolder MOZ_FINAL + : public nsIDumpGCAndCCLogsCallback { public: NS_DECL_ISUPPORTS explicit nsDumpGCAndCCLogsCallbackHolder(nsIDumpGCAndCCLogsCallback* aCallback) : mCallback(aCallback) { }
--- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -1168,46 +1168,46 @@ nsMemoryReporterManager::GetReportsExten aHandleReport, aHandleReportData, aFinishReporting, aFinishReportingData, aDMDDumpIdent); } if (aMinimize) { - rv = MinimizeMemoryUsage(NS_NewRunnableMethod(this, &nsMemoryReporter