Commit 2fd402b7 authored by haraken@chromium.org's avatar haraken@chromium.org

Blink-in-JS: Support private scripts in partial interfaces (PrivateScriptRunner part)

This CL is a split from https://codereview.chromium.org/457483002/.
This CL supports private scripts in partial interfaces.

Assume that XPartial-1 and XPartial-2 are partial interfaces of X.
Then we have XPartial-1.js, XPartial-2.js and X.js.
When a DOM attribute/method in any of the JS files is requested
at the first time, all of the JS files are compiled at the same time.

This is redundant because when a DOM attribute/method in XPartial-1.js
is requested, we just need to compile XPartial-1.js and X.js.
I'll address the issue in a follow-up CL.

BUG=341031
TEST=fast/dom/private-script-unittest.html

Review URL: https://codereview.chromium.org/454773002

git-svn-id: svn://svn.chromium.org/blink/trunk@179960 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent ba0ce51a
...@@ -42,6 +42,9 @@ PASS privateScriptTest.stringAttributeImplementedInCPP is "undefined" ...@@ -42,6 +42,9 @@ PASS privateScriptTest.stringAttributeImplementedInCPP is "undefined"
PASS privateScriptTest.stringAttributeImplementedInCPP is "foo" PASS privateScriptTest.stringAttributeImplementedInCPP is "foo"
PASS privateScriptTest.addIntegerImplementedInCPPForPrivateScriptOnly is undefined. PASS privateScriptTest.addIntegerImplementedInCPPForPrivateScriptOnly is undefined.
PASS privateScriptTest.stringAttributeImplementedInCPPForPrivateScriptOnly is undefined. PASS privateScriptTest.stringAttributeImplementedInCPPForPrivateScriptOnly is undefined.
PASS privateScriptTest.addIntegerInPartial(111, 222) is 333
PASS privateScriptTest.addInteger2InPartial(111, 222) is 333
PASS privateScriptTest.stringAttributeInPartial is "foo"
PASS successfullyParsed is true PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
......
...@@ -81,6 +81,11 @@ shouldBeEqualToString('privateScriptTest.stringAttributeImplementedInCPP', 'foo' ...@@ -81,6 +81,11 @@ shouldBeEqualToString('privateScriptTest.stringAttributeImplementedInCPP', 'foo'
shouldBeUndefined('privateScriptTest.addIntegerImplementedInCPPForPrivateScriptOnly'); shouldBeUndefined('privateScriptTest.addIntegerImplementedInCPPForPrivateScriptOnly');
shouldBeUndefined('privateScriptTest.stringAttributeImplementedInCPPForPrivateScriptOnly'); shouldBeUndefined('privateScriptTest.stringAttributeImplementedInCPPForPrivateScriptOnly');
shouldBe('privateScriptTest.addIntegerInPartial(111, 222)', '333');
shouldBe('privateScriptTest.addInteger2InPartial(111, 222)', '333');
privateScriptTest.stringAttributeInPartial = "foo";
shouldBeEqualToString('privateScriptTest.stringAttributeInPartial', 'foo');
</script> </script>
</body> </body>
</html> </html>
...@@ -20,7 +20,8 @@ core_static_interface_idl_files = ...@@ -20,7 +20,8 @@ core_static_interface_idl_files =
core_idl_files + core_idl_files +
webcore_testing_idl_files webcore_testing_idl_files
core_static_dependency_idl_files = core_static_dependency_idl_files =
core_dependency_idl_files core_dependency_idl_files +
webcore_testing_dependency_idl_files
# Generated IDL files # Generated IDL files
core_generated_interface_idl_files = core_generated_interface_idl_files =
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
], ],
'core_static_dependency_idl_files': [ 'core_static_dependency_idl_files': [
'<@(core_dependency_idl_files)', '<@(core_dependency_idl_files)',
'<@(webcore_testing_dependency_idl_files)',
], ],
# Generated IDL files # Generated IDL files
......
...@@ -20,41 +20,68 @@ namespace blink { ...@@ -20,41 +20,68 @@ namespace blink {
#define LOG_ERROR_ALWAYS(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) #define LOG_ERROR_ALWAYS(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__)
static v8::Handle<v8::Value> compilePrivateScript(v8::Isolate* isolate, String className) static v8::Handle<v8::Value> compileAndRunInternalScript(v8::Isolate* isolate, String className, const unsigned char* source, size_t size)
{ {
size_t index; v8::TryCatch block;
#ifndef NDEBUG String sourceString(reinterpret_cast<const char*>(source), size);
for (index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSourcesForTesting); index++) { v8::Handle<v8::Value> result = V8ScriptRunner::compileAndRunInternalScript(v8String(isolate, sourceString), isolate);
if (className == kPrivateScriptSourcesForTesting[index].name) if (block.HasCaught()) {
break; LOG_ERROR_ALWAYS("Private script error: Compile failed. (Class name = %s)\n", className.utf8().data());
if (!block.Message().IsEmpty())
LOG_ERROR_ALWAYS("%s\n", toCoreString(block.Message()->Get()).utf8().data());
RELEASE_ASSERT_NOT_REACHED();
} }
if (index != WTF_ARRAY_LENGTH(kPrivateScriptSourcesForTesting)) { return result;
String source(reinterpret_cast<const char*>(kPrivateScriptSourcesForTesting[index].source), kPrivateScriptSourcesForTesting[index].size); }
return V8ScriptRunner::compileAndRunInternalScript(v8String(isolate, source), isolate);
// FIXME: If we have X.js, XPartial-1.js and XPartial-2.js, currently all of the JS files
// are compiled when any of the JS files is requested. Ideally we should avoid compiling
// unrelated JS files. For example, if a method in XPartial-1.js is requested, we just
// need to compile X.js and XPartial-1.js, and don't need to compile XPartial-2.js.
static void compilePrivateScript(v8::Isolate* isolate, String className)
{
int compiledScriptCount = 0;
// |kPrivateScriptSourcesForTesting| is defined in V8PrivateScriptSources.h, which is auto-generated
// by make_private_script_source.py.
#ifndef NDEBUG
for (size_t index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSourcesForTesting); index++) {
if (className == kPrivateScriptSourcesForTesting[index].dependencyClassName) {
compileAndRunInternalScript(isolate, className, kPrivateScriptSourcesForTesting[index].source, kPrivateScriptSourcesForTesting[index].size);
compiledScriptCount++;
}
} }
#endif #endif
// |kPrivateScriptSources| is defined in V8PrivateScriptSources.h, which is auto-generated // |kPrivateScriptSources| is defined in V8PrivateScriptSources.h, which is auto-generated
// by make_private_script.py. // by make_private_script_source.py.
for (index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources); index++) { for (size_t index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources); index++) {
if (className == kPrivateScriptSources[index].name) if (className == kPrivateScriptSources[index].dependencyClassName) {
break; compileAndRunInternalScript(isolate, className, kPrivateScriptSources[index].source, kPrivateScriptSources[index].size);
compiledScriptCount++;
}
} }
if (index == WTF_ARRAY_LENGTH(kPrivateScriptSources)) {
if (!compiledScriptCount) {
LOG_ERROR_ALWAYS("Private script error: Target source code was not found. (Class name = %s)\n", className.utf8().data()); LOG_ERROR_ALWAYS("Private script error: Target source code was not found. (Class name = %s)\n", className.utf8().data());
RELEASE_ASSERT_NOT_REACHED(); RELEASE_ASSERT_NOT_REACHED();
} }
}
v8::TryCatch block; static v8::Handle<v8::Value> compilePrivateScriptRunner(v8::Isolate* isolate)
String source(reinterpret_cast<const char*>(kPrivateScriptSources[index].source), kPrivateScriptSources[index].size); {
v8::Handle<v8::Value> result = V8ScriptRunner::compileAndRunInternalScript(v8String(isolate, source), isolate); const String className = "PrivateScriptRunner";
if (block.HasCaught()) { size_t index;
LOG_ERROR_ALWAYS("Private script error: Compile failed. (Class name = %s)\n", className.utf8().data()); // |kPrivateScriptSources| is defined in V8PrivateScriptSources.h, which is auto-generated
if (!block.Message().IsEmpty()) // by make_private_script_source.py.
LOG_ERROR_ALWAYS("%s\n", toCoreString(block.Message()->Get()).utf8().data()); for (index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources); index++) {
if (className == kPrivateScriptSources[index].className)
break;
}
if (index == WTF_ARRAY_LENGTH(kPrivateScriptSources)) {
LOG_ERROR_ALWAYS("Private script error: Target source code was not found. (Class name = %s)\n", className.utf8().data());
RELEASE_ASSERT_NOT_REACHED(); RELEASE_ASSERT_NOT_REACHED();
} }
return result; return compileAndRunInternalScript(isolate, className, kPrivateScriptSources[index].source, kPrivateScriptSources[index].size);
} }
static v8::Handle<v8::Object> classObjectOfPrivateScript(ScriptState* scriptState, String className) static v8::Handle<v8::Object> classObjectOfPrivateScript(ScriptState* scriptState, String className)
...@@ -66,7 +93,7 @@ static v8::Handle<v8::Object> classObjectOfPrivateScript(ScriptState* scriptStat ...@@ -66,7 +93,7 @@ static v8::Handle<v8::Object> classObjectOfPrivateScript(ScriptState* scriptStat
if (compiledClass.IsEmpty()) { if (compiledClass.IsEmpty()) {
v8::Handle<v8::Value> installedClasses = scriptState->perContextData()->compiledPrivateScript("PrivateScriptRunner"); v8::Handle<v8::Value> installedClasses = scriptState->perContextData()->compiledPrivateScript("PrivateScriptRunner");
if (installedClasses.IsEmpty()) { if (installedClasses.IsEmpty()) {
installedClasses = compilePrivateScript(isolate, "PrivateScriptRunner"); installedClasses = compilePrivateScriptRunner(isolate);
scriptState->perContextData()->setCompiledPrivateScript("PrivateScriptRunner", installedClasses); scriptState->perContextData()->setCompiledPrivateScript("PrivateScriptRunner", installedClasses);
} }
RELEASE_ASSERT(!installedClasses.IsEmpty()); RELEASE_ASSERT(!installedClasses.IsEmpty());
......
...@@ -82,7 +82,8 @@ function privateScriptClass() ...@@ -82,7 +82,8 @@ function privateScriptClass()
function installClass(className, implementation) function installClass(className, implementation)
{ {
installedClasses[className] = new privateScriptClass(); if (!(className in installedClasses))
installedClasses[className] = new privateScriptClass();
implementation(window, installedClasses[className]); implementation(window, installedClasses[className]);
} }
......
...@@ -10,9 +10,27 @@ python make_private_script_source.py DESTINATION_FILE SOURCE_FILES ...@@ -10,9 +10,27 @@ python make_private_script_source.py DESTINATION_FILE SOURCE_FILES
""" """
import os import os
import re
import sys import sys
# We assume that X.js has a corresponding X.idl in the same directory.
# If X is a partial interface, this method extracts the base name of the partial interface from X.idl.
# Otherwise, this method returns None.
def extract_partial_interface_name(filename):
basename, ext = os.path.splitext(filename)
assert ext == '.js'
# PrivateScriptRunner.js is a special JS script to control private scripts,
# and doesn't have a corresponding IDL file.
if os.path.basename(basename) == 'PrivateScriptRunner':
return None
idl_filename = basename + '.idl'
with open(idl_filename) as f:
contents = f.read()
match = re.search(r'partial\s+interface\s+(\w+)\s*{', contents)
return match and match.group(1)
def main(): def main():
output_filename = sys.argv[1] output_filename = sys.argv[1]
input_filenames = sys.argv[2:] input_filenames = sys.argv[2:]
...@@ -24,11 +42,12 @@ def main(): ...@@ -24,11 +42,12 @@ def main():
with open(input_filename) as input_file: with open(input_filename) as input_file:
input_text = input_file.read() input_text = input_file.read()
hex_values = ['0x{0:02x}'.format(ord(char)) for char in input_text] hex_values = ['0x{0:02x}'.format(ord(char)) for char in input_text]
contents.append('const unsigned char kSourceOf%s[] = {\n %s\n};\n' % ( contents.append('const unsigned char kSourceOf%s[] = {\n %s\n};\n\n' % (
class_name, ', '.join(hex_values))) class_name, ', '.join(hex_values)))
contents.append('struct %s {' % source_name) contents.append('struct %s {' % source_name)
contents.append(""" contents.append("""
const char* name; const char* className;
const char* dependencyClassName;
const unsigned char* source; const unsigned char* source;
size_t size; size_t size;
}; };
...@@ -37,7 +56,8 @@ def main(): ...@@ -37,7 +56,8 @@ def main():
contents.append('struct %s k%s[] = {\n' % (source_name, source_name)) contents.append('struct %s k%s[] = {\n' % (source_name, source_name))
for input_filename in input_filenames: for input_filename in input_filenames:
class_name, ext = os.path.splitext(os.path.basename(input_filename)) class_name, ext = os.path.splitext(os.path.basename(input_filename))
contents.append(' { "%s", kSourceOf%s, sizeof(kSourceOf%s) },\n' % (class_name, class_name, class_name)) dependency_class_name = extract_partial_interface_name(input_filename) or class_name
contents.append(' { "%s", "%s", kSourceOf%s, sizeof(kSourceOf%s) },\n' % (class_name, dependency_class_name, class_name, class_name))
contents.append('};\n') contents.append('};\n')
with open(output_filename, 'w') as output_file: with open(output_filename, 'w') as output_file:
......
...@@ -949,7 +949,8 @@ action("make_core_generated_private_script_for_testing") { ...@@ -949,7 +949,8 @@ action("make_core_generated_private_script_for_testing") {
script = "../build/scripts/make_private_script_source.py" script = "../build/scripts/make_private_script_source.py"
inputs = [ inputs = [
"testing/PrivateScriptTest.js", "testing/PartialPrivateScriptTest.js",
"testing/PrivateScriptTest.js",
] ]
outputs = [ outputs = [
"$blink_core_output_dir/PrivateScriptSourcesForTesting.h", "$blink_core_output_dir/PrivateScriptSourcesForTesting.h",
......
...@@ -31,6 +31,8 @@ webcore_dom_files = get_path_info(_gypi.webcore_dom_files, "abspath") ...@@ -31,6 +31,8 @@ webcore_dom_files = get_path_info(_gypi.webcore_dom_files, "abspath")
webcore_html_files = get_path_info(_gypi.webcore_html_files, "abspath") webcore_html_files = get_path_info(_gypi.webcore_html_files, "abspath")
webcore_svg_files = get_path_info(_gypi.webcore_svg_files, "abspath") webcore_svg_files = get_path_info(_gypi.webcore_svg_files, "abspath")
webcore_testing_idl_files = get_path_info(_gypi.webcore_testing_idl_files, "abspath") webcore_testing_idl_files = get_path_info(_gypi.webcore_testing_idl_files, "abspath")
webcore_testing_dependency_idl_files =
get_path_info(_gypi.webcore_testing_dependency_idl_files, "abspath")
generated_webcore_testing_idl_files = generated_webcore_testing_idl_files =
get_path_info(_gypi.generated_webcore_testing_idl_files, "abspath") get_path_info(_gypi.generated_webcore_testing_idl_files, "abspath")
webcore_testing_files = get_path_info(_gypi.webcore_testing_files, "abspath") webcore_testing_files = get_path_info(_gypi.webcore_testing_files, "abspath")
......
...@@ -3337,6 +3337,9 @@ ...@@ -3337,6 +3337,9 @@
'testing/RefCountedScriptWrappable.idl', 'testing/RefCountedScriptWrappable.idl',
'testing/TypeConversions.idl', 'testing/TypeConversions.idl',
], ],
'webcore_testing_dependency_idl_files': [
'testing/PartialPrivateScriptTest.idl',
],
'generated_webcore_testing_idl_files': [ 'generated_webcore_testing_idl_files': [
'<(blink_core_output_dir)/InternalRuntimeFlags.idl', '<(blink_core_output_dir)/InternalRuntimeFlags.idl',
'<(blink_core_output_dir)/InternalSettingsGenerated.idl', '<(blink_core_output_dir)/InternalSettingsGenerated.idl',
...@@ -3363,6 +3366,7 @@ ...@@ -3363,6 +3366,7 @@
'testing/MockPagePopupDriver.h', 'testing/MockPagePopupDriver.h',
'testing/NullExecutionContext.cpp', 'testing/NullExecutionContext.cpp',
'testing/NullExecutionContext.h', 'testing/NullExecutionContext.h',
'testing/PartialPrivateScriptTest.h',
'testing/PrivateScriptTest.cpp', 'testing/PrivateScriptTest.cpp',
'testing/PrivateScriptTest.h', 'testing/PrivateScriptTest.h',
'testing/RefCountedScriptWrappable.cpp', 'testing/RefCountedScriptWrappable.cpp',
......
...@@ -145,45 +145,46 @@ ...@@ -145,45 +145,46 @@
'actions': [ 'actions': [
{ {
'action_name': 'generatePrivateScript', 'action_name': 'generatePrivateScript',
# FIXME: The implementation of Blink-in-JS is not yet mature. # FIXME: The implementation of Blink-in-JS is not yet mature.
# You can use Blink-in-JS in your local experiment, but don't ship it. # You can use Blink-in-JS in your local experiment, but don't ship it.
# crbug.com/341031 # crbug.com/341031
'private_script_files': [ 'private_script_files': [
'../bindings/core/v8/PrivateScriptRunner.js', '../bindings/core/v8/PrivateScriptRunner.js',
'../core/html/HTMLMarqueeElement.js', '../core/html/HTMLMarqueeElement.js',
], ],
'inputs': [ 'inputs': [
'../build/scripts/make_private_script_source.py', '../build/scripts/make_private_script_source.py',
'<@(_private_script_files)', '<@(_private_script_files)',
], ],
'outputs': [ 'outputs': [
'<(blink_core_output_dir)/PrivateScriptSources.h', '<(blink_core_output_dir)/PrivateScriptSources.h',
], ],
'action': [ 'action': [
'python', 'python',
'../build/scripts/make_private_script_source.py', '../build/scripts/make_private_script_source.py',
'<@(_outputs)', '<@(_outputs)',
'<@(_private_script_files)' '<@(_private_script_files)'
], ],
}, },
{ {
'action_name': 'generatePrivateScriptForTesting', 'action_name': 'generatePrivateScriptForTesting',
'private_script_files': [ 'private_script_files': [
'testing/PrivateScriptTest.js', 'testing/PartialPrivateScriptTest.js',
'testing/PrivateScriptTest.js',
],
'inputs': [
'../build/scripts/make_private_script_source.py',
'<@(_private_script_files)',
],
'outputs': [
'<(blink_core_output_dir)/PrivateScriptSourcesForTesting.h',
],
'action': [
'python',
'../build/scripts/make_private_script_source.py',
'<@(_outputs)',
'<@(_private_script_files)'
], ],
'inputs': [
'../build/scripts/make_private_script_source.py',
'<@(_private_script_files)',
],
'outputs': [
'<(blink_core_output_dir)/PrivateScriptSourcesForTesting.h',
],
'action': [
'python',
'../build/scripts/make_private_script_source.py',
'<@(_outputs)',
'<@(_private_script_files)'
],
}, },
{ {
'action_name': 'HTMLEntityTable', 'action_name': 'HTMLEntityTable',
......
// Copyright 2014 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 PartialPrivateScriptTest_h
#define PartialPrivateScriptTest_h
namespace blink {
// FIXME: This header file is needed because auto-generated binding tries to include
// PartialPrivateScriptTest.h. We should improve the IDL compiler and remove this file.
class PartialPrivateScriptTest {
};
} // namespace blink
#endif
// Copyright 2014 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.
partial interface PrivateScriptTest {
[ImplementedInPrivateScript] short addIntegerInPartial(short value1, short value2);
[ImplementedInPrivateScript] short addInteger2InPartial(short value1, short value2);
[ImplementedInPrivateScript] attribute DOMString stringAttributeInPartial;
};
// Copyright 2014 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.
"use strict";
installClass("PrivateScriptTest", function(global, PrivateScriptTestPrototype) {
PrivateScriptTestPrototype.addIntegerInPartial = function(value1, value2) {
return value1 + value2;
}
PrivateScriptTestPrototype.addInteger2InPartial = function(value1, value2) {
// addValue_ is a method defined in PrivateScriptTest.js.
// Partial interfaces should be able to use methods defined in the base interface.
return this.addValues_(value1, value2);
}
Object.defineProperty(PrivateScriptTestPrototype, "stringAttributeInPartial", {
get: function() { return this.m_stringAttributeInPartial; },
set: function(value) { this.m_stringAttributeInPartial = value; }
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment