diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index c686e6b8b4346d..70490754eeca9d 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn @@ -191,11 +191,7 @@ android_assets("pak_file_assets") { # These assets are needed by both monochrome and stand alone WebView, but not by # Chrome. android_assets("monochrome_webview_assets") { - sources = [ - webview_license_path, - ] deps = [ - ":generate_webview_license_notice", "//third_party/icu:icu_assets", "//v8:v8_external_startup_data_assets", ] diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn new file mode 100644 index 00000000000000..a846f82f1a5151 --- /dev/null +++ b/android_webview/apk/BUILD.gn @@ -0,0 +1,15 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +# Since Monochrome has its own content provider, these two files are put +# in two different targets. +android_library("webview_license_provider_java") { + java_files = [ "//android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java" ] +} + +android_library("webview_license_activity_java") { + java_files = [ "//android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java" ] +} diff --git a/android_webview/glue/java/src/com/android/webview/chromium/LicenseActivity.java b/android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java similarity index 72% rename from android_webview/glue/java/src/com/android/webview/chromium/LicenseActivity.java rename to android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java index 7607a94e3794d3..7427d0d1f662a4 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/LicenseActivity.java +++ b/android_webview/apk/java/src/com/android/webview/chromium/LicenseActivity.java @@ -17,18 +17,20 @@ * other than LicenseContentProvider. */ public class LicenseActivity extends Activity { + private static final String LICENSES_URI_SUFFIX = "LicenseContentProvider/webview_licenses"; + private static final String LICENSES_CONTENT_TYPE = "text/html"; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final String packageName = getPackageName(); final Intent intent = new Intent(Intent.ACTION_VIEW); - final String licenseUri = String.format("content://%s.%s", - packageName, LicenseContentProvider.LICENSES_URI_SUFFIX); - intent.setDataAndType( - Uri.parse(licenseUri), LicenseContentProvider.LICENSES_CONTENT_TYPE); + final String licenseUri = + String.format("content://%s.%s", packageName, LICENSES_URI_SUFFIX); + intent.setDataAndType(Uri.parse(licenseUri), LICENSES_CONTENT_TYPE); intent.addCategory(Intent.CATEGORY_DEFAULT); - final int titleId = getResources().getIdentifier( - "license_activity_title", "string", packageName); + final int titleId = + getResources().getIdentifier("license_activity_title", "string", packageName); if (titleId != 0) { intent.putExtra(Intent.EXTRA_TITLE, getString(titleId)); } diff --git a/android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java similarity index 95% rename from android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java rename to android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java index 0571e7cb49f941..ef2dd2f56d1392 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/LicenseContentProvider.java +++ b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java @@ -68,8 +68,7 @@ public String getType(Uri uri) { } @Override - public int update(Uri uri, ContentValues values, String where, - String[] whereArgs) { + public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { throw new UnsupportedOperationException(); } @@ -84,8 +83,8 @@ public Uri insert(Uri uri, ContentValues values) { } @Override - public Cursor query(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) { + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { throw new UnsupportedOperationException(); } -} +} \ No newline at end of file diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn index ab529541f9bea2..b1147387350047 100644 --- a/android_webview/glue/BUILD.gn +++ b/android_webview/glue/BUILD.gn @@ -61,8 +61,6 @@ android_library("glue") { "java/src/com/android/webview/chromium/DrawGLFunctor.java", "java/src/com/android/webview/chromium/GeolocationPermissionsAdapter.java", "java/src/com/android/webview/chromium/GraphicsUtils.java", - "java/src/com/android/webview/chromium/LicenseActivity.java", - "java/src/com/android/webview/chromium/LicenseContentProvider.java", "java/src/com/android/webview/chromium/MonochromeLibraryPreloader.java", "java/src/com/android/webview/chromium/ServiceWorkerClientAdapter.java", "java/src/com/android/webview/chromium/ServiceWorkerControllerAdapter.java", diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index 6998087442f2c6..566de377fead41 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni @@ -122,9 +122,11 @@ template("monochrome_public_apk_tmpl") { } deps += [ "//android_webview:monochrome_webview_assets", + "//android_webview/apk:webview_license_activity_java", "//android_webview/glue", "//chrome/android:chrome_public_non_pak_assets", "//chrome/android:monochrome_pak_assets", + "//chrome/android/monochrome:monochrome_license_provider_java", ] if (!is_java_debug) { diff --git a/chrome/android/monochrome/BUILD.gn b/chrome/android/monochrome/BUILD.gn new file mode 100644 index 00000000000000..2553c5d7c13f79 --- /dev/null +++ b/chrome/android/monochrome/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("monochrome_license_provider_java") { + java_files = + [ "java/src/com/android/webview/chromium/LicenseContentProvider.java" ] + + deps = [ + "//base:base_java", + "//chrome/android:chrome_java", + "//components/about_ui/android:aboutui_java", + ] +} diff --git a/chrome/android/monochrome/java/DEPS b/chrome/android/monochrome/java/DEPS new file mode 100644 index 00000000000000..69fc97884b55f5 --- /dev/null +++ b/chrome/android/monochrome/java/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/about_ui/android/java", +] diff --git a/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java b/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java new file mode 100644 index 00000000000000..5bc5c683583cb5 --- /dev/null +++ b/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java @@ -0,0 +1,99 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.android.webview.chromium; + +import android.annotation.TargetApi; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.library_loader.ProcessInitException; +import org.chromium.chrome.browser.init.ChromeBrowserInitializer; +import org.chromium.components.aboutui.CreditUtils; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Content provider for the OSS licenses file. + */ +@TargetApi(Build.VERSION_CODES.KITKAT) +public class LicenseContentProvider + extends ContentProvider implements ContentProvider.PipeDataWriter { + public static final String LICENSES_URI_SUFFIX = "LicenseContentProvider/webview_licenses"; + public static final String LICENSES_CONTENT_TYPE = "text/html"; + private static final String TAG = "LicenseCP"; + + @Override + public boolean onCreate() { + return true; + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + if (uri != null && uri.toString().endsWith(LICENSES_URI_SUFFIX)) { + return openPipeHelper(null, null, null, "webview_licenses.notice", this); + } + return null; + } + + @Override + public void writeDataToPipe( + ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, String filename) { + try (OutputStream out = new FileOutputStream(output.getFileDescriptor());) { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + try { + ChromeBrowserInitializer.getInstance(getContext()) + .handleSynchronousStartup(); + } catch (ProcessInitException e) { + Log.e(TAG, "Fail to initialize the Chrome Browser.", e); + } + } + }); + out.write(CreditUtils.nativeGetJavaWrapperCredits()); + } catch (IOException e) { + Log.e(TAG, "Failed to write the license file", e); + } + } + + @Override + public String getType(Uri uri) { + if (uri != null && uri.toString().endsWith(LICENSES_URI_SUFFIX)) { + return LICENSES_CONTENT_TYPE; + } + return null; + } + + @Override + public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { + throw new UnsupportedOperationException(); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException(); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException(); + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + throw new UnsupportedOperationException(); + } +} diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 9dff41e6e3ae99..11a6a60d10d9dd 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn @@ -486,6 +486,7 @@ split_static_library("ui") { "//chrome/common:instant_mojom", "//chrome/common/net", "//chrome/installer/util:with_no_strings", + "//components/about_ui", "//components/app_modal", "//components/autofill/content/browser:risk_proto", "//components/autofill/core/browser", diff --git a/chrome/browser/ui/webui/DEPS b/chrome/browser/ui/webui/DEPS index e54b94adcde4fc..f866a1e74fc4f9 100644 --- a/chrome/browser/ui/webui/DEPS +++ b/chrome/browser/ui/webui/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/about_ui" "+components/invalidation", "+components/onc", "+components/proximity_auth", diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc index 671826764f4e48..1216a0739d3be3 100644 --- a/chrome/browser/ui/webui/about_ui.cc +++ b/chrome/browser/ui/webui/about_ui.cc @@ -49,6 +49,7 @@ #include "chrome/grit/browser_resources.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" +#include "components/about_ui/credit_utils.h" #include "components/grit/components_resources.h" #include "components/strings/grit/components_locale_settings.h" #include "content/public/browser/browser_thread.h" @@ -691,32 +692,14 @@ void AboutUIHTMLSource::StartDataRequest( idr = IDR_KEYBOARD_UTILS_JS; #endif - base::StringPiece raw_response = - ResourceBundle::GetSharedInstance().GetRawDataResource(idr); if (idr == IDR_ABOUT_UI_CREDITS_HTML) { - const uint8_t* next_encoded_byte = - reinterpret_cast(raw_response.data()); - size_t input_size_remaining = raw_response.size(); - BrotliDecoderState* decoder = - BrotliDecoderCreateInstance(nullptr /* no custom allocator */, - nullptr /* no custom deallocator */, - nullptr /* no custom memory handle */); - CHECK(!!decoder); - while (!BrotliDecoderIsFinished(decoder)) { - size_t output_size_remaining = 0; - CHECK(BrotliDecoderDecompressStream( - decoder, &input_size_remaining, &next_encoded_byte, - &output_size_remaining, nullptr, - nullptr) != BROTLI_DECODER_RESULT_ERROR); - const uint8_t* output_buffer = - BrotliDecoderTakeOutput(decoder, &output_size_remaining); - response.insert(response.end(), output_buffer, - output_buffer + output_size_remaining); - } - BrotliDecoderDestroyInstance(decoder); + response = about_ui::GetCredits(true /*include_scripts*/); } else { - response = raw_response.as_string(); + response = ResourceBundle::GetSharedInstance() + .GetRawDataResource(idr) + .as_string(); } + #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) } else if (source_name_ == chrome::kChromeUIDiscardsHost) { response = AboutDiscards(path); diff --git a/components/about_ui/BUILD.gn b/components/about_ui/BUILD.gn new file mode 100644 index 00000000000000..b1b59494a3873a --- /dev/null +++ b/components/about_ui/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +static_library("about_ui") { + sources = [ + "credit_utils.cc", + "credit_utils.h", + ] + deps = [ + "//base", + "//components/resources:components_resources", + "//third_party/brotli:dec", + "//ui/base", + "//ui/resources", + ] + + if (is_android) { + deps += [ "//components/about_ui/android:about_ui_jni_headers" ] + } +} diff --git a/components/about_ui/DEPS b/components/about_ui/DEPS new file mode 100644 index 00000000000000..72b42ddc984fda --- /dev/null +++ b/components/about_ui/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+components/grit/components_resources.h", + "+jni", + "+third_party/brotli", + "+ui/base", +] diff --git a/components/about_ui/android/BUILD.gn b/components/about_ui/android/BUILD.gn new file mode 100644 index 00000000000000..7e82547127eab6 --- /dev/null +++ b/components/about_ui/android/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +import("//build/config/android/rules.gni") + +generate_jni("about_ui_jni_headers") { + sources = [ + "java/src/org/chromium/components/aboutui/CreditUtils.java", + ] + jni_package = "components/about_ui" +} + +android_library("aboutui_java") { + java_files = [ "java/src/org/chromium/components/aboutui/CreditUtils.java" ] + deps = [ + "//base:base_java", + ] +} diff --git a/components/about_ui/android/OWNERS b/components/about_ui/android/OWNERS new file mode 100644 index 00000000000000..d99c934847cb76 --- /dev/null +++ b/components/about_ui/android/OWNERS @@ -0,0 +1,2 @@ +agrieve@chromium.org +torne@chromium.org diff --git a/components/about_ui/android/java/src/org/chromium/components/aboutui/CreditUtils.java b/components/about_ui/android/java/src/org/chromium/components/aboutui/CreditUtils.java new file mode 100644 index 00000000000000..b2be4adb04b4ce --- /dev/null +++ b/components/about_ui/android/java/src/org/chromium/components/aboutui/CreditUtils.java @@ -0,0 +1,16 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.aboutui; + +import org.chromium.base.annotations.JNINamespace; + +/** Credits-related utilities. */ +@JNINamespace("about_ui") +public class CreditUtils { + private CreditUtils() {} + + /** Returns a string containing the content of about_credits.html. */ + public static native byte[] nativeGetJavaWrapperCredits(); +} diff --git a/components/about_ui/credit_utils.cc b/components/about_ui/credit_utils.cc new file mode 100644 index 00000000000000..238512d62ef869 --- /dev/null +++ b/components/about_ui/credit_utils.cc @@ -0,0 +1,71 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/about_ui/credit_utils.h" + +#include + +#include "base/strings/string_piece.h" +#include "components/grit/components_resources.h" +#include "third_party/brotli/include/brotli/decode.h" +#include "ui/base/resource/resource_bundle.h" + +#if defined(OS_ANDROID) +#include "base/android/jni_array.h" +#include "jni/CreditUtils_jni.h" +#endif + +namespace about_ui { + +std::string GetCredits(bool include_scripts) { + std::string response; + base::StringPiece raw_response = + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_ABOUT_UI_CREDITS_HTML); + const uint8_t* next_encoded_byte = + reinterpret_cast(raw_response.data()); + size_t input_size_remaining = raw_response.size(); + BrotliDecoderState* decoder = BrotliDecoderCreateInstance( + nullptr /* no custom allocator */, nullptr /* no custom deallocator */, + nullptr /* no custom memory handle */); + CHECK(!!decoder); + while (!BrotliDecoderIsFinished(decoder)) { + size_t output_size_remaining = 0; + CHECK(BrotliDecoderDecompressStream( + decoder, &input_size_remaining, &next_encoded_byte, + &output_size_remaining, nullptr, + nullptr) != BROTLI_DECODER_RESULT_ERROR); + const uint8_t* output_buffer = + BrotliDecoderTakeOutput(decoder, &output_size_remaining); + response.insert(response.end(), output_buffer, + output_buffer + output_size_remaining); + } + BrotliDecoderDestroyInstance(decoder); + if (include_scripts) { + response += + "\n\n" + "\n"; + } + response += "\n"; + return response; +} + +#if defined(OS_ANDROID) +static base::android::ScopedJavaLocalRef GetJavaWrapperCredits( + JNIEnv* env, + const base::android::JavaParamRef& clazz) { + std::string html_content = GetCredits(false); + const char* html_content_arr = html_content.c_str(); + return base::android::ToJavaByteArray( + env, reinterpret_cast(html_content_arr), + html_content.size()); +} + +// The RegisterNativesImpl is a static function, so has to be called somewhere. +bool RegisterAboutUIUtils(JNIEnv* env) { + return RegisterNativesImpl(env); +} +#endif + +} // namespace about_ui diff --git a/components/about_ui/credit_utils.h b/components/about_ui/credit_utils.h new file mode 100644 index 00000000000000..2bc934547a8183 --- /dev/null +++ b/components/about_ui/credit_utils.h @@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_ +#define COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_ + +#include + +namespace about_ui { + +// Decode a Brotli compressed HTML license file and attach .js files. +std::string GetCredits(bool include_scripts); + +} // namespace about_ui + +#endif // COMPONENTS_ABOUT_UI_CREDIT_UTILS_H_ diff --git a/components/about_ui/resources/about_credits.js b/components/about_ui/resources/about_credits.js index d01ffb6851410d..3339a7e591784c 100644 --- a/components/about_ui/resources/about_credits.js +++ b/components/about_ui/resources/about_credits.js @@ -2,56 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/* eslint-disable no-restricted-properties */ function $(id) { return document.getElementById(id); } - -function toggle(o) { - var licence = o.nextSibling; - - while (licence.className != 'licence') { - if (!licence) return false; - licence = licence.nextSibling; - } - - if (licence.style && licence.style.display == 'block') { - licence.style.display = 'none'; - o.textContent = 'show license'; - } else { - licence.style.display = 'block'; - o.textContent = 'hide license'; - } - return false; -} +/* eslint-enable no-restricted-properties */ document.addEventListener('DOMContentLoaded', function() { - var licenseEls = [].slice.call(document.getElementsByClassName('product')); - - licenseEls.sort(function(a, b) { - var nameA = a.getElementsByClassName('title')[0].textContent; - var nameB = b.getElementsByClassName('title')[0].textContent; - if (nameA < nameB) return -1; - if (nameA > nameB) return 1; - return 0; - }); - - var parentEl = licenseEls[0].parentNode; - parentEl.innerHTML = ''; - for (var i = 0; i < licenseEls.length; i++) { - parentEl.appendChild(licenseEls[i]); - } - - document.body.hidden = false; - if (cr.isChromeOS) { var keyboardUtils = document.createElement('script'); keyboardUtils.src = 'chrome://credits/keyboard_utils.js'; document.body.appendChild(keyboardUtils); } - var links = document.querySelectorAll('a.show'); - for (var i = 0; i < links.length; ++i) { - links[i].onclick = function() { return toggle(this); }; - } - + $('print-link').hidden = false; $('print-link').onclick = function() { window.print(); return false; diff --git a/components/about_ui/resources/about_credits.tmpl b/components/about_ui/resources/about_credits.tmpl index fc4fc965ac9ecb..5aa5c9b7f7bb26 100644 --- a/components/about_ui/resources/about_credits.tmpl +++ b/components/about_ui/resources/about_credits.tmpl @@ -2,6 +2,7 @@ + Credits - + Credits -Print +
{{entries}}
- - - - \ No newline at end of file diff --git a/components/about_ui/resources/about_credits_entry.tmpl b/components/about_ui/resources/about_credits_entry.tmpl index d1810cd9402945..51aa4685dd4068 100644 --- a/components/about_ui/resources/about_credits_entry.tmpl +++ b/components/about_ui/resources/about_credits_entry.tmpl @@ -1,9 +1,9 @@
{{name}} -show license homepage + +
{{license}}
- diff --git a/tools/licenses.py b/tools/licenses.py index bf0b53bc9c313b..ec63ff4c113634 100755 --- a/tools/licenses.py +++ b/tools/licenses.py @@ -555,6 +555,19 @@ def EvaluateTemplate(template, env, escape=True): template = template.replace('{{%s}}' % key, val) return template + def MetadataToTemplateEntry(metadata, entry_template, entry_id): + env = { + 'name': metadata['Name'], + 'url': metadata['URL'], + 'license': open(metadata['License File'], 'rb').read(), + 'id': str(entry_id), + } + return { + 'name': metadata['Name'], + 'content': EvaluateTemplate(entry_template, env), + 'license_file': metadata['License File'], + } + if gn_target: third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target) @@ -575,7 +588,17 @@ def EvaluateTemplate(template, env, escape=True): 'about_credits_entry.tmpl') entry_template = open(entry_template_file).read() + entry_id = 0 entries = [] + # Start from Chromium's LICENSE file + chromium_license_metadata = { + 'Name': 'The Chromium Project', + 'URL': 'http://www.chromium.org', + 'License File': os.path.join(_REPOSITORY_ROOT, 'LICENSE') } + entries.append(MetadataToTemplateEntry(chromium_license_metadata, + entry_template, entry_id)) + entry_id += 1 + for path in third_party_dirs: try: metadata = ParseDir(path, _REPOSITORY_ROOT) @@ -592,22 +615,11 @@ def EvaluateTemplate(template, env, escape=True): # updated to provide --gn-target to this script. if path in KNOWN_NON_IOS_LIBRARIES: continue - env = { - 'name': metadata['Name'], - 'url': metadata['URL'], - 'license': open(metadata['License File'], 'rb').read(), - } - entry = { - 'name': metadata['Name'], - 'content': EvaluateTemplate(entry_template, env), - 'license_file': metadata['License File'], - } - entries.append(entry) - # Sort by size in order to improve gzip compression ratio (puts similar - # licenses near each other). The licenses are re-sorted by the JavaScript - # when loaded. - entries.sort(key=lambda entry: (len(entry['content']), - entry['name'], entry['content'])) + entries.append(MetadataToTemplateEntry(metadata, entry_template, + entry_id)) + entry_id += 1 + + entries.sort(key=lambda entry: (entry['name'], entry['content'])) entries_contents = '\n'.join([entry['content'] for entry in entries]) file_template = open(file_template_file).read()