Skip to content

Commit

Permalink
Pass shared values when processing multipart responses.
Browse files Browse the repository at this point in the history
This change allows values from one part of a multipart response to be handled
during processing of subsequent parts.

The motivating use case is to allow a response to specify a "name" to use during
CSS-based animation once and have it apply to all subsequent parts.
For example instead of:

```json
[{"name": "my-page", "body": {"id-1": "content-1"}},
 {"name": "my-page", "body": {"id-2": "content-2"}},
 {"name": "my-page", "body": {"id-3": "content-3"}}]
```

The following would now be processed as if the above were sent:

```json
[{"name": "my-page", "body": {"id-1": "content-1"}},
 {"body": {"id-2": "content-2"}},
 {"body": {"id-3": "content-3"}}]
```

Progress on youtube#299.

SAVEPOINT

handle shared values

revert server

revert islated client changes

revert parts of spf.nav

revert parts of spf.nav.response
  • Loading branch information
nicksay committed Mar 2, 2015
1 parent 3ccf28e commit 68b2a2a
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 29 deletions.
20 changes: 12 additions & 8 deletions src/client/nav/nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -663,9 +663,10 @@ spf.nav.handleNavigateError_ = function(options, url, err) {
* @param {spf.nav.Info} info The navigation info object.
* @param {string} url The requested URL, without the SPF identifier.
* @param {spf.SingleResponse} partial The partial response object.
* @param {!Object.<string>} shared A shared values object.
* @private
*/
spf.nav.handleNavigatePart_ = function(options, info, url, partial) {
spf.nav.handleNavigatePart_ = function(options, info, url, partial, shared) {
// Reload if the "part process" event is canceled.
if (!spf.nav.dispatchPartProcess_(url, partial, options)) {
spf.nav.reload(url, spf.nav.ReloadReason.PART_PROCESS_CANCELED);
Expand All @@ -685,7 +686,7 @@ spf.nav.handleNavigatePart_ = function(options, info, url, partial) {
}

try {
spf.nav.response.process(url, partial, info, function() {
spf.nav.response.process(url, partial, info, shared, function() {
spf.nav.dispatchPartDone_(url, partial, options);
});
} catch (err) {
Expand Down Expand Up @@ -749,7 +750,7 @@ spf.nav.handleNavigateSuccess_ = function(options, info, url, response) {
// so an empty object is used to ensure events/callbacks are properly
// queued after existing ones from any ongoing part prcoessing.
var r = /** @type {spf.SingleResponse} */ (multipart ? {} : response);
spf.nav.response.process(url, r, info, function() {
spf.nav.response.process(url, r, info, null, function() {
// If this navigation was from history, attempt to scroll to the previous
// position after all processing is complete. This should not be done
// earlier because the prevous position might rely on page width/height
Expand Down Expand Up @@ -1032,9 +1033,11 @@ spf.nav.handleLoadError_ = function(isPrefetch, options, info, url, err) {
* @param {spf.nav.Info} info The navigation info object.
* @param {string} url The requested URL, without the SPF identifier.
* @param {spf.SingleResponse} partial The partial response object.
* @param {!Object.<string>} shared A shared values object.
* @private
*/
spf.nav.handleLoadPart_ = function(isPrefetch, options, info, url, partial) {
spf.nav.handleLoadPart_ = function(isPrefetch, options, info, url, partial,
shared) {
// Abort the load/prefetch if the "part process" callback is canceled.
// Note: pass "true" to only execute callbacks and not dispatch events.
if (!spf.nav.dispatchPartProcess_(url, partial, options, true)) {
Expand Down Expand Up @@ -1080,7 +1083,7 @@ spf.nav.handleLoadPart_ = function(isPrefetch, options, info, url, partial) {
var processFn = isPrefetch ?
spf.nav.response.preprocess :
spf.nav.response.process;
processFn(url, partial, info, function() {
processFn(url, partial, info, shared, function() {
// Note: pass "true" to only execute callbacks and not dispatch events.
spf.nav.dispatchPartDone_(url, partial, options, true);
});
Expand Down Expand Up @@ -1164,7 +1167,7 @@ spf.nav.handleLoadSuccess_ = function(isPrefetch, options, info, url,
// so an empty object is used to ensure the callback is properly
// queued after existing ones from any ongoing part prcoessing.
var r = /** @type {spf.SingleResponse} */ (multipart ? {} : response);
processFn(url, r, info, function() {
processFn(url, r, info, null, function() {
// Note: pass "true" to only execute callbacks and not dispatch events.
spf.nav.dispatchDone_(url, response, options, true);
});
Expand Down Expand Up @@ -1226,14 +1229,15 @@ spf.nav.process = function(response, opt_callback) {
};
if (multipart) {
var parts = response['parts'];
var shared = response; // Top-level properties act as shared values.
for (var i = 0; i < parts.length; i++) {
var fn = spf.bind(done, null, i, parts.length - 1);
spf.nav.response.process(url, parts[i], null, fn);
spf.nav.response.process(url, parts[i], null, shared, fn);
}
} else {
response = /** @type {spf.SingleResponse} */ (response);
var fn = spf.bind(done, null, 0, 0);
spf.nav.response.process(url, response, null, fn);
spf.nav.response.process(url, response, null, null, fn);
}
};

Expand Down
69 changes: 51 additions & 18 deletions src/client/nav/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,9 @@ spf.nav.request.handleResponseFromCache_ = function(url, options, timing,
}
if (options.onPart && response['type'] == 'multipart') {
var parts = response['parts'];
var shared = response; // Top-level properties act as shared values.
for (var i = 0; i < parts.length; i++) {
options.onPart(url, parts[i]);
options.onPart(url, parts[i], shared);
}
}
spf.nav.request.done_(url, options, timing, response, updateCache);
Expand Down Expand Up @@ -269,10 +270,11 @@ spf.nav.request.handleChunkFromXHR_ = function(url, options, chunking,
}
return;
}
if (options.onPart) {
for (var i = 0; i < parsed.parts.length; i++) {
spf.debug.debug(' parsed part', parsed.parts[i]);
options.onPart(url, parsed.parts[i]);
for (var i = 0; i < parsed.parts.length; i++) {
spf.debug.debug(' parsed part', parsed.parts[i]);
spf.nav.request.copySharedFrom_(chunking.shared, parsed.parts[i]);
if (options.onPart) {
options.onPart(url, parsed.parts[i], chunking.shared);
}
}
chunking.complete = chunking.complete.concat(parsed.parts);
Expand Down Expand Up @@ -363,33 +365,27 @@ spf.nav.request.handleCompleteFromXHR_ = function(url, options, timing,
}
return;
}
if (options.onPart && parts.length > 1) {
// Only execute callbacks for parts that have not already been processed.
if (parts.length > 1) {
// In case there is an edge case where some parts were parsed on-the-fly
// but the entire response needed a full parse here, start iteration where
// the chunk processing left off. This is mostly a safety measure and
// the number of chunks processed here should be 0.
for (var i = chunking.complete.length; i < parts.length; i++) {
spf.debug.debug(' parsed part', parts[i]);
options.onPart(url, parts[i]);
spf.nav.request.copySharedFrom_(chunking.shared, parts[i]);
if (options.onPart) {
// Only execute callbacks for parts that have not been processed.
options.onPart(url, parts[i], chunking.shared);
}
}
}
var response;
if (parts.length > 1) {
var cacheType;
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
if (part['cacheType']) {
cacheType = part['cacheType'];
}
}
response = /** @type {spf.MultipartResponse} */ ({
'parts': parts,
'type': 'multipart'
});
if (cacheType) {
response['cacheType'] = cacheType;
}
spf.nav.request.copySharedTo_(response, chunking.shared);
} else if (parts.length == 1) {
response = /** @type {spf.SingleResponse} */(parts[0]);
} else {
Expand Down Expand Up @@ -431,6 +427,37 @@ spf.nav.request.done_ = function(url, options, timing, response, cache) {
};


/**
* Copy shared values from a response.
*
* @param {!Object.<string>} shared The shared values.
* @param {spf.SingleResponse} response The response.
* @private
*/
spf.nav.request.copySharedFrom_ = function(shared, response) {
if ('cacheType' in response) {
shared['cacheType'] = response['cacheType'];
}
if ('name' in response) {
shared['name'] = response['name'];
}
};


/**
* Copy shared values to a response.
*
* @param {spf.SingleResponse} response The response.
* @param {!Object.<string>} shared The shared values.
* @private
*/
spf.nav.request.copySharedTo_ = function(response, shared) {
for (var k in shared) {
response[k] = shared[k];
}
};


/**
* @param {string} url The requested URL, without the SPF identifier.
* @param {string|null|undefined} opt_current The current page's URL. Some
Expand Down Expand Up @@ -556,6 +583,12 @@ spf.nav.request.Chunking_ = function() {
* @type {!Array}
*/
this.complete = [];
/**
* Values for response fields that are present in one part but meant to be
* shared across all (e.g. cacheType, name).
* @type {!Object.<string>}
*/
this.shared = {};
};


Expand Down
10 changes: 7 additions & 3 deletions src/client/nav/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,14 @@ spf.nav.response.parse = function(text, opt_multipart, opt_lastDitch) {
* @param {string} url The URL of the response being processed.
* @param {spf.SingleResponse} response The SPF response object to process.
* @param {spf.nav.Info=} opt_info The navigation info object.
* @param {Object.<string>=} opt_shared A shared values object.
* @param {function(string, spf.SingleResponse)=} opt_callback Function to
* execute when processing is done; the first argument is `url`,
* the second argument is `response`.
*/
spf.nav.response.process = function(url, response, opt_info, opt_callback) {
spf.debug.info('nav.response.process ', response, opt_info);
spf.nav.response.process = function(url, response, opt_info, opt_shared,
opt_callback) {
spf.debug.info('nav.response.process ', response, opt_info, opt_shared);

var isNavigate = opt_info && spf.string.startsWith(opt_info.type, 'navigate');
var isReverse = opt_info && opt_info.reverse;
Expand Down Expand Up @@ -362,11 +364,13 @@ spf.nav.response.process = function(url, response, opt_info, opt_callback) {
* @param {string} url The URL of the response being preprocessed.
* @param {spf.SingleResponse} response The SPF response object to preprocess.
* @param {spf.nav.Info=} opt_info The navigation info object.
* @param {Object.<string>=} opt_shared A shared values object.
* @param {function(string, spf.SingleResponse)=} opt_callback Function to
* execute when preprocessing is done; the first argument is `url`,
* the second argument is `response`.
*/
spf.nav.response.preprocess = function(url, response, opt_info, opt_callback) {
spf.nav.response.preprocess = function(url, response, opt_info, opt_shared,
opt_callback) {
spf.debug.info('nav.response.preprocess ', response);
// Convert the URL to absolute, to be used for finding the task queue.
var key = 'preprocess ' + spf.url.absolute(url);
Expand Down

0 comments on commit 68b2a2a

Please sign in to comment.