You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by li...@apache.org on 2010/02/02 01:13:50 UTC
svn commit: r905469 - in /incubator/shindig/trunk: config/ php/config/
php/src/gadgets/ php/src/gadgets/render/ php/src/gadgets/servlet/
Author: lindner
Date: Tue Feb 2 00:13:49 2010
New Revision: 905469
URL: http://svn.apache.org/viewvc?rev=905469&view=rev
Log:
SHINDIG-1260 | Patch from Jacky Wang | Improper JavaScript inclusion logic leads to out-of-order and duplicate includes / JavaScript errors
Modified:
incubator/shindig/trunk/config/container.js
incubator/shindig/trunk/php/config/container.php
incubator/shindig/trunk/php/src/gadgets/GadgetContext.php
incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php
incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php
incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetRenderer.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetUrlRenderer.php
incubator/shindig/trunk/php/src/gadgets/servlet/JsServlet.php
Modified: incubator/shindig/trunk/config/container.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/config/container.js?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/config/container.js (original)
+++ incubator/shindig/trunk/config/container.js Tue Feb 2 00:13:49 2010
@@ -114,7 +114,7 @@
},
"rpc" : {
// Path to the relay file. Automatically appended to the parent
- /// parameter if it passes input validation and is not null.
+ // parameter if it passes input validation and is not null.
// This should never be on the same host in a production environment!
// Only use this for TESTING!
"parentRelayUrl" : "/gadgets/files/container/rpc_relay.html",
Modified: incubator/shindig/trunk/php/config/container.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/config/container.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/config/container.php (original)
+++ incubator/shindig/trunk/php/config/container.php Tue Feb 2 00:13:49 2010
@@ -110,7 +110,7 @@
'jsondb_path' => realpath(dirname(__FILE__) . '/../../javascript/sampledata') . '/canonicaldb.json',
// Force these libraries to be external (included through <script src="..."> tags), this way they could be cached by the browser
- 'focedJsLibs' => '',
+ 'forcedJsLibs' => '',
// After checking the internal __autoload function, shindig can also call the 'extension_autoloader' function to load an
// unknown custom class, this is particuarly useful for when intergrating shindig into an existing framework that also depends on autoloading
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetContext.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetContext.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetContext.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetContext.php Tue Feb 2 00:13:49 2010
@@ -46,7 +46,7 @@
// Request variables
$this->setIgnoreCache($this->getIgnoreCacheParam());
- $this->setForcedJsLibs($this->getFocedJsLibsParam());
+ $this->setForcedJsLibs($this->getForcedJsLibsParam());
$this->setUrl($this->getUrlParam());
$this->setModuleId($this->getModuleIdParam());
$this->setView($this->getViewParam());
@@ -79,7 +79,7 @@
return (isset($_GET['nocache']) && intval($_GET['nocache']) == 1) || (isset($_GET['bpc']) && intval($_GET['bpc']) == 1);
}
- private function getFocedJsLibsParam() {
+ private function getForcedJsLibsParam() {
return isset($_GET['libs']) ? trim($_GET['libs']) : null;
}
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php Tue Feb 2 00:13:49 2010
@@ -70,7 +70,9 @@
*/
private function parseFeatures(Gadget &$gadget) {
$found = $missing = array();
- if (! $this->context->getRegistry()->resolveFeatures(array_merge($gadget->gadgetSpec->requiredFeatures, $gadget->gadgetSpec->optionalFeatures), $found, $missing)) {
+ if (! $this->context->getRegistry()->resolveFeatures(
+ array_merge($gadget->gadgetSpec->requiredFeatures, $gadget->gadgetSpec->optionalFeatures),
+ $found, $missing, true)) {
$requiredMissing = false;
foreach ($missing as $featureName) {
if (in_array($featureName, $gadget->gadgetSpec->requiredFeatures)) {
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php Tue Feb 2 00:13:49 2010
@@ -33,6 +33,14 @@
$this->registerFeatures($featurePath);
}
+ public function getFeaturesContent($features, GadgetContext $context, $isGadgetContext) {
+ $ret = '';
+ foreach ($features as $feature) {
+ $ret .= $this->getFeatureContent($feature, $context, $isGadgetContext);
+ }
+ return $ret;
+ }
+
public function getFeatureContent($feature, GadgetContext $context, $isGadgetContext) {
if (empty($feature)) return '';
if (!isset($this->features[$feature])) {
@@ -78,7 +86,7 @@
return $ret;
}
- public function resolveFeatures($needed, &$resultsFound, &$resultsMissing) {
+ public function resolveFeatures($needed, &$resultsFound, &$resultsMissing, $isExpand) {
$resultsFound = array();
$resultsMissing = array();
if (! count($needed)) {
@@ -91,18 +99,77 @@
if ($feature == null) {
$resultsMissing[] = $featureName;
} else {
- $this->addFeatureToResults($resultsFound, $feature);
+ $this->addFeatureToResults($resultsFound, $feature, $isExpand);
}
}
return count($resultsMissing) == 0;
}
+
+ public function sortFeatures($features, $forcedJsLibs, &$sortedFeatureGroups) {
+ $sortedFeatureGroups = array();
+ if (empty($features)) return;
- private function addFeatureToResults(&$results, $feature) {
+ // Topological sorting with constrain
+ // Build the topology matrix
+ $sortedFeatures = array();
+ $reverseDeps = array();
+ foreach ($features as $feature) {
+ $reverseDeps[$feature] = array();
+ }
+ $depCount = array();
+ foreach ($features as $feature) {
+ $deps = $this->features[$feature]['deps'];
+ $deps = array_uintersect($deps, $features, "strcasecmp");
+ $depCount[$feature] = count($deps);
+ foreach ($deps as $dep) {
+ $reverseDeps[$dep][] = $feature;
+ }
+ }
+
+ // iterate to do the sorting
+ $prevIsForcedJsLibs = true;
+ $featureQueue = array();
+ while (! empty($depCount)) {
+ $fail = true;
+ while (! empty($depCount)) { // get grouped features greedily
+ $foundAll = true;
+ foreach ($depCount as $feature => $count) {
+ if ($count != 0) continue;
+ $fail = false; // found at least one root node
+ if ((! $prevIsForcedJsLibs) ^ in_array($feature, $forcedJsLibs)) {
+ $foundAll = false;
+ $featureQueue[] = $feature;
+ foreach ($reverseDeps[$feature] as $reverseDep) {
+ $depCount[$reverseDep] -= 1;
+ }
+ unset($depCount[$feature]);
+ }
+ }
+ if ($foundAll) break;
+ }
+ if ($fail) {
+ throw new GadgetException("Sorting feature dependence failed: it contains ring!");
+ }
+ if (! empty($featureQueue)) {
+ if ($prevIsForcedJsLibs) {
+ $sortedFeatureGroups[] = array('type' => 'external', 'features' => $featureQueue);
+ } else {
+ $sortedFeatureGroups[] = array('type' => 'inline', 'features' => $featureQueue);
+ }
+ }
+ $prevIsForcedJsLibs = ! $prevIsForcedJsLibs;
+ $featureQueue = array();
+ }
+ }
+
+ private function addFeatureToResults(&$results, $feature, $isExpand) {
if (in_array($feature['name'], $results)) {
return;
}
- foreach ($feature['deps'] as $dep) {
- $this->addFeatureToResults($results, $this->features[$dep]);
+ if ($isExpand) {
+ foreach ($feature['deps'] as $dep) {
+ $this->addFeatureToResults($results, $this->features[$dep], true);
+ }
}
if (!in_array($feature['name'], $results)) {
$results[] = $feature['name'];
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php Tue Feb 2 00:13:49 2010
@@ -215,7 +215,8 @@
*/
private function parseFeatures(DOMElement &$modulePrefs, GadgetSpec &$gadget) {
$gadget->requiredFeatures = $gadget->optionalFeatures = array();
- if (($requiredNodes = $modulePrefs->getElementsByTagName('Require')) != null) {
+ $requiredNodes = $modulePrefs->getElementsByTagName('Require');
+ if ($requiredNodes->length != 0) {
foreach ($requiredNodes as $requiredFeature) {
$gadget->requiredFeatures[] = $requiredFeature->getAttribute('feature');
if ($requiredFeature->getAttribute('feature') == 'content-rewrite') {
@@ -225,7 +226,8 @@
}
}
}
- if (($optionalNodes = $modulePrefs->getElementsByTagName('Optional')) != null) {
+ $optionalNodes = $modulePrefs->getElementsByTagName('Optional');
+ if ($optionalNodes->length != 0) {
foreach ($optionalNodes as $optionalFeature) {
$gadget->optionalFeatures[] = $optionalFeature->getAttribute('feature');
// Content-rewrite is a special case since it has Params as child nodes
Modified: incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php Tue Feb 2 00:13:49 2010
@@ -298,26 +298,27 @@
}
public function getJavaScripts() {
+ $registry = $this->context->getRegistry();
$forcedJsLibs = $this->getForcedJsLibs();
- $externalScript = false;
- if (! empty($forcedJsLibs)) {
- // if some of the feature libraries are externalized (through a browser cachable <script src="/gadgets/js/opensocial-0.9:settitle.js"> type url)
- // we inject the tag and don't inline those libs (and their dependencies)
- $forcedJsLibs = explode(':', $forcedJsLibs);
- $externalScript = Config::get('default_js_prefix') . $this->getJsUrl($forcedJsLibs, $this->gadget) . "&container=" . $this->context->getContainer();
- $registry = $this->context->getRegistry();
- $missing = array();
- $registry->resolveFeatures($forcedJsLibs, $forcedJsLibs, $missing);
- }
- $script = '';
- foreach ($this->gadget->features as $feature) {
- if (! is_array($forcedJsLibs) || (is_array($forcedJsLibs) && ! in_array($feature, $forcedJsLibs))) {
- $script .= $this->context->getRegistry()->getFeatureContent($feature, $this->context, true);
+ $sortedFeatureGroups = array();
+ $registry->sortFeatures($this->gadget->features, $forcedJsLibs, $sortedFeatureGroups);
+ $scripts = array();
+
+ // if some of the feature libraries are externalized (through a browser cachable <script src="/gadgets/js/opensocial-0.9:settitle.js"> type url)
+ // we inject the tag and don't inline those libs (and their dependencies)
+ foreach ($sortedFeatureGroups as $featureGroup) {
+ if ($featureGroup['type'] == 'inline') {
+ $featureGroup['content'] = $registry->getFeaturesContent($featureGroup['features'], $this->context, true);
+ } else {
+ $featureGroup['content'] = Config::get('default_js_prefix') . $this->getJsUrl($featureGroup['features']) . "&container=" . $this->context->getContainer();
}
+ $scripts[] = $featureGroup;
}
+
+ $script = '';
// Add the JavaScript initialization strings for the configuration, localization and preloads
$script .= "\n";
- $script .= $this->appendJsConfig($this->gadget, count($forcedJsLibs));
+ $script .= $this->appendJsConfig($this->gadget, $sortedFeatureGroups);
$script .= $this->appendMessages($this->gadget);
$script .= $this->appendPreloads($this->gadget);
if (count($this->dataInserts)) {
@@ -330,7 +331,11 @@
if ($this->gadget->gadgetSpec->templatesDisableAutoProcessing) {
$script .= "opensocial.template.Container.disableAutoProcessing();\n";
}
- return array('inline' => $script, 'external' => $externalScript);
+ $scripts[] = array(
+ 'type' => 'inline',
+ 'content' => $script
+ );
+ return $scripts;
}
/**
@@ -348,62 +353,73 @@
// Inject the OpenSocial feature javascripts
$scripts = $this->getJavaScripts();
- if ($scripts['external']) {
+ foreach ($scripts as $script) {
$scriptNode = $doc->createElement('script');
- $scriptNode->setAttribute('src', $scripts['external']);
+ if ($script['type'] == 'inline') {
+ $scriptNode->setAttribute('type', 'text/javascript');
+ $scriptNode->appendChild($doc->createTextNode($script['content']));
+ } else {
+ $scriptNode->setAttribute('src', $script['content']);
+ }
$node->appendChild($scriptNode);
}
- $scriptNode = $doc->createElement('script');
- $scriptNode->setAttribute('type', 'text/javascript');
- $scriptNode->appendChild($doc->createTextNode($scripts['inline']));
- $node->appendChild($scriptNode);
}
/**
* Retrieve the forced javascript libraries (if any), using either the &libs= from the query
* or if that's empty, from the config
*
- * @return unknown
+ * @return array contains the names of forced external javascript libs.
*/
private function getForcedJsLibs() {
$forcedJsLibs = $this->context->getForcedJsLibs();
// allow the &libs=.. param to override our forced js libs configuration value
if (empty($forcedJsLibs)) {
- $forcedJsLibs = Config::get('focedJsLibs');
+ $forcedJsLibs = Config::get('forcedJsLibs');
+ }
+ if (empty($forcedJsLibs)) {
+ return array();
+ } else {
+ return explode(':', $forcedJsLibs);
}
- return $forcedJsLibs;
}
/**
* Appends the javascript features configuration string
*
* @param Gadget $gadget
- * @param unknown_type $hasForcedLibs
+ * @param $featureGroups
* @return string
*/
- private function appendJsConfig(Gadget $gadget, $hasForcedLibs) {
+ private function appendJsConfig(Gadget $gadget, $featureGroups) {
$container = $this->context->getContainer();
$containerConfig = $this->context->getContainerConfig();
- //TODO some day we should parse the forcedLibs too, and include their config selectivly as well for now we just include everything if forced libs is set.
- if ($hasForcedLibs) {
- $gadgetConfig = $containerConfig->getConfig($container, 'gadgets.features');
- } else {
- $gadgetConfig = array();
- $featureConfig = $containerConfig->getConfig($container, 'gadgets.features');
- foreach ($gadget->getJsLibraries() as $library) {
- $feature = $library->getFeatureName();
- if (! isset($gadgetConfig[$feature]) && ! empty($featureConfig[$feature])) {
- $gadgetConfig[$feature] = $featureConfig[$feature];
- }
+ $forcedJsLibs = $this->getForcedJsLibs();
+ $gadgetConfig = array();
+ $featureConfig = $containerConfig->getConfig($container, 'gadgets.features');
+
+ // TODO some day we should parse the forcedLibs too, and include their config selectivly as well.
+ // For now we just include everything.
+ $features = array();
+ foreach ($featureGroups as $featureGroup) {
+ $features = array_merge($features, $featureGroup['features']);
+ }
+
+ foreach ($features as $feature) {
+ if (! isset($gadgetConfig[$feature]) && ! empty($featureConfig[$feature])) {
+ $gadgetConfig[$feature] = $featureConfig[$feature];
}
}
+
// Add gadgets.util support. This is calculated dynamically based on request inputs.
// See java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java for reference.
$requires = array();
- foreach ($gadget->features as $feature) {
+ foreach ($features as $feature) {
$requires[$feature] = new EmptyClass();
}
$gadgetConfig['core.util'] = $requires;
+
+ // following are some quick-fixes for osml and osapi.
if (isset($gadgetConfig['osml'])) {
unset($gadgetConfig['osml']);
}
Modified: incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php Tue Feb 2 00:13:49 2010
@@ -50,13 +50,16 @@
// Manually generate the html document using basic string concatinations instead of using our DOM based functions
$content .= "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n";
$content .= '<style>'.Config::get('gadget_css')."</style>\n";
+
$scripts = $this->getJavaScripts();
- if ($scripts['external']) {
- $content .= "<script type=\"text/javascript\" src=\"{$scripts['external']}\"></script>\n";
- }
- if (!empty($scripts['inline'])) {
- $content .= "<script type=\"text/javascript\">{$scripts['inline']}</script>\n";
+ foreach ($scripts as $script) {
+ if ($script['type'] == 'inline') {
+ $content .= "<script type=\"text/javascript\">{$script['content']}</script>\n";
+ } else {
+ $content .= "<script type=\"text/javascript\" src=\"{$script['content']}\"></script>\n";
+ }
}
+
$content .= "</head>\n<body>\n";
$content .= $gadget->substitutions->substitute($view['content']);
$content .= '<script type="text/javascript">'.$this->getBodyScript()."</script>\n";
Modified: incubator/shindig/trunk/php/src/gadgets/render/GadgetRenderer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/render/GadgetRenderer.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/render/GadgetRenderer.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/render/GadgetRenderer.php Tue Feb 2 00:13:49 2010
@@ -38,12 +38,10 @@
* @return string the list of libraries in core:caja:etc.js?v=checksum> format
*/
protected function getJsUrl($features) {
- $ret = '';
if (! is_array($features) || ! count($features)) {
- $ret = 'core';
- } else {
- $ret = implode(':', $features);
+ return 'null';
}
+ $ret = implode(':', $features);
$cache = Cache::createCache(Config::get('feature_cache'), 'FeatureCache');
if (($md5 = $cache->get(md5('getJsUrlMD5'))) === false) {
$registry = $this->context->getRegistry();
Modified: incubator/shindig/trunk/php/src/gadgets/render/GadgetUrlRenderer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/render/GadgetUrlRenderer.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/render/GadgetUrlRenderer.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/render/GadgetUrlRenderer.php Tue Feb 2 00:13:49 2010
@@ -22,7 +22,7 @@
/**
* Renders an 'URL' type view (where the iframe is redirected to the specified url)
- * This is more a legacy iGoogle support feature then something that should be actually
+ * This is more a legacy iGoogle support feature than something that should be actually
* used. Proxied content is the socially aware (and higher performance) version of this
* See GadgetHrefRenderer for it's implementation.
*
@@ -35,13 +35,21 @@
$queryStr = strpos($redirURI, '?') !== false ? substr($redirURI, strpos($redirURI, '?')) : '';
$query = $queryStr;
$query .= $this->getPrefsQueryString($gadget->gadgetSpec->userPrefs);
+
+ // deal with features
+ $registry = $this->context->getRegistry();
+ // since the URL mode doesn't actually have the gadget XML body, it can't inline
+ // the javascript content anyway - thus could us just ignore the 'forcedJsLibs' part.
+ $forcedJsLibs = array();
+ $sortedFeatureGroups = array();
+ $registry->sortFeatures($gadget->features, $forcedJsLibs, $sortedFeatureGroups);
+
+ // join the groups
$features = array();
- $forcedLibs = Config::get('focedJsLibs');
- if ($forcedLibs == null) {
- $features = $gadget->features;
- } else {
- $features = explode(':', $forcedLibs);
+ foreach ($sortedFeatureGroups as $featureGroup) {
+ $features = array_merge($features, $featureGroup['features']);
}
+
$query .= $this->appendLibsToQuery($features);
$query .= '&lang=' . urlencode(isset($_GET['lang']) ? $_GET['lang'] : 'en');
$query .= '&country=' . urlencode(isset($_GET['country']) ? $_GET['country'] : 'US');
Modified: incubator/shindig/trunk/php/src/gadgets/servlet/JsServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/servlet/JsServlet.php?rev=905469&r1=905468&r2=905469&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/servlet/JsServlet.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/servlet/JsServlet.php Tue Feb 2 00:13:49 2010
@@ -25,7 +25,7 @@
/**
* This event handler deals with the /js/core:caja:etc.js request which content type=url gadgets can use
* to retrieve our features javascript code, or used to make the most frequently used part of the feature
- * library external, and hence cachable by the browser
+ * library external, and hence cachable by the browser.
*/
class JsServlet extends HttpServlet {
@@ -56,7 +56,7 @@
$missing = array();
$context = new GadgetContext('GADGET');
$registry = new GadgetFeatureRegistry(Config::get('features_path'));
- if ($registry->resolveFeatures($needed, $found, $missing)) {
+ if ($registry->resolveFeatures($needed, $found, $missing, false)) {
$isGadgetContext = !isset($_GET["c"]) || $_GET['c'] == 0 ? true : false;
$jsData = '';
foreach ($found as $feature) {