mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-29 08:34:54 +00:00
6d21d83244
Move ve.track() subscriber to its own file, and have it route mwtiming.* events (for TimingData) and mwedit.* events (for Edit schema) differently. Most of the data population lives in the subscriber, so actual ve.track() calls are pretty lightweight. Existing ve.track() calls with timing data were kept with their names intact for backwards compatibility, but we may eventually want to throw them out and start from scratch. ve.init.mw.ViewPageTarget.init.js: * Remove old track subscriber * Track init and ready events * Remove old ve.track( 'Edit', ... ) crap that didn't work ve.init.mw.ViewPageTarget.js: * Fire the saveWorkflowBegin event before the save dialog loads rather than after * Remove unnecessary this.events.trackSaveError() calls: TargetEvents already listens to these events itself * Remove badtoken handler because all it was was an unnecessary trackSaveError() call * Add abort tracking ** Pass trackMechanism through deactivate() and cancel() ve.init.mw.Target.js: * Add static.integrationType to populate the 'integration' field in the schema ve.init.mw.TargetEvents.js: * Simplify onSaveError* methods away into connect bindings * Map track topics to mwtiming.* so they can be routed separately * Track save-related mwedit.* events Depends on I978eda96c in WikimediaEvents Change-Id: Iae677d9b15c71d2b18e795bd5179d11876c06abd
206 lines
6.4 KiB
JavaScript
206 lines
6.4 KiB
JavaScript
/*!
|
|
* VisualEditor MediaWiki Initialization class.
|
|
*
|
|
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* Initialization MediaWiki Target Analytics.
|
|
*
|
|
* @class
|
|
*
|
|
* @constructor
|
|
* @param {ve.init.mw.Target} target Target class to log events for
|
|
*/
|
|
ve.init.mw.TargetEvents = function ( target ) {
|
|
this.target = target;
|
|
this.timings = { saveRetries: 0 };
|
|
// Events
|
|
this.target.connect( this, {
|
|
saveWorkflowBegin: 'onSaveWorkflowBegin',
|
|
saveWorkflowEnd: 'onSaveWorkflowEnd',
|
|
saveInitiated: 'onSaveInitated',
|
|
save: 'onSaveComplete',
|
|
saveReview: 'onSaveReview',
|
|
saveErrorEmpty: [ 'trackSaveError', 'empty' ],
|
|
saveErrorSpamBlacklist: [ 'trackSaveError', 'spamblacklist' ],
|
|
saveErrorAbuseFilter: [ 'trackSaveError', 'abusefilter' ],
|
|
saveErrorBadToken: [ 'trackSaveError', 'badtoken' ],
|
|
saveErrorNewUser: [ 'trackSaveError', 'newuser' ],
|
|
saveErrorCaptcha: [ 'trackSaveError', 'captcha' ],
|
|
saveErrorUnknown: [ 'trackSaveError', 'unknown' ],
|
|
editConflict: [ 'trackSaveError', 'editconflict' ],
|
|
surfaceReady: 'onSurfaceReady',
|
|
showChanges: 'onShowChanges',
|
|
showChangesError: 'onShowChangesError',
|
|
noChanges: 'onNoChanges',
|
|
serializeComplete: 'onSerializeComplete',
|
|
serializeError: 'onSerializeError'
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* Target specific ve.track wrapper
|
|
*
|
|
* @param {string} topic Event name
|
|
* @param {Object} data Additional data describing the event, encoded as an object
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.track = function ( topic, data ) {
|
|
data.targetName = this.target.constructor.static.name;
|
|
ve.track( 'mwtiming.' + topic, data );
|
|
|
|
if ( topic.indexOf( 'performance.system.serializeforcache' ) === 0 ) {
|
|
// HACK: track serializeForCache duration here, because there's no event for that
|
|
this.timings.serializeForCache = data.duration;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Track when user begins the save workflow
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onSaveWorkflowBegin = function () {
|
|
this.timings.saveWorkflowBegin = ve.now();
|
|
this.track( 'behavior.lastTransactionTillSaveDialogOpen', {
|
|
duration: this.timings.saveWorkflowBegin - this.timings.lastTransaction
|
|
} );
|
|
ve.track( 'mwedit.saveIntent' );
|
|
};
|
|
|
|
/**
|
|
* Track when user ends the save workflow
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onSaveWorkflowEnd = function () {
|
|
this.track( 'behavior.saveDialogClose', { duration: ve.now() - this.timings.saveWorkflowBegin } );
|
|
this.timings.saveWorkflowBegin = null;
|
|
};
|
|
|
|
/**
|
|
* Track when document save is initiated
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onSaveInitated = function () {
|
|
this.timings.saveInitiated = ve.now();
|
|
this.timings.saveRetries++;
|
|
this.track( 'behavior.saveDialogOpenTillSave', {
|
|
duration: this.timings.saveInitiated - this.timings.saveWorkflowBegin
|
|
} );
|
|
ve.track( 'mwedit.saveAttempt' );
|
|
};
|
|
|
|
/**
|
|
* Track when the save is complete
|
|
* @param {string} content
|
|
* @param {string} categoriesHtml
|
|
* @param {number} newRevId
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onSaveComplete = function ( content, categoriesHtml, newRevId ) {
|
|
this.track( 'performance.user.saveComplete', { duration: ve.now() - this.timings.saveInitiated } );
|
|
this.timings.saveRetries = 0;
|
|
ve.track( 'mwedit.saveSuccess', {
|
|
timing: ve.now() - this.timings.saveInitiated + ( this.timings.serializeForCache || 0 ),
|
|
'page.revid': newRevId
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* Track a save error by type
|
|
*
|
|
* @method
|
|
* @param {string} type Text for error type
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.trackSaveError = function ( type ) {
|
|
var key,
|
|
// Maps mwtiming types to mwedit types
|
|
typeMap = {
|
|
badtoken: 'userBadToken',
|
|
newuser: 'userNewUser',
|
|
abusefilter: 'extensionAbuseFilter',
|
|
captcha: 'extensionCaptcha',
|
|
spamblacklist: 'extensionSpamBlacklist',
|
|
empty: 'responseEmpty',
|
|
unknown: 'responseUnknown',
|
|
editconflict: 'editConflict'
|
|
},
|
|
// Types that are logged as performance.user.saveError.{type}
|
|
// (for historical reasons; this sucks)
|
|
specialTypes = [ 'editconflict' ];
|
|
|
|
key = 'performance.user.saveError';
|
|
if ( specialTypes.indexOf( type ) !== -1 ) {
|
|
key += '.' + type;
|
|
}
|
|
this.track( key, {
|
|
duration: ve.now() - this.timings.saveInitiated,
|
|
retries: this.timings.saveRetries,
|
|
type: type
|
|
} );
|
|
|
|
ve.track( 'mwedit.saveFailure', {
|
|
type: typeMap[type] || 'responseUnknown',
|
|
timing: ve.now() - this.timings.saveInitiated + ( this.timings.serializeForCache || 0 )
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* Record the time of the last transaction in response to a 'transact' event on the document.
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.recordLastTransactionTime = function () {
|
|
this.timings.lastTransaction = ve.now();
|
|
};
|
|
|
|
/**
|
|
* Track time elapsed from beginning of save workflow to review
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onSaveReview = function () {
|
|
this.timings.saveReview = ve.now();
|
|
this.track( 'behavior.saveDialogOpenTillReview', {
|
|
duration: this.timings.saveReview - this.timings.saveWorkflowBegin
|
|
} );
|
|
};
|
|
|
|
ve.init.mw.TargetEvents.prototype.onSurfaceReady = function () {
|
|
this.track( 'performance.system.activation', { duration: ve.now() - this.timings.activationStart } );
|
|
this.target.surface.getModel().getDocument().connect( this, {
|
|
transact: 'recordLastTransactionTime'
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* Track when the user enters the review workflow
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onShowChanges = function () {
|
|
this.track( 'performance.user.reviewComplete', { duration: ve.now() - this.timings.saveReview } );
|
|
};
|
|
|
|
/**
|
|
* Track when the diff request fails in the review workflow
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onShowChangesError = function () {
|
|
this.track( 'performance.user.reviewError', { duration: ve.now() - this.timings.saveReview } );
|
|
};
|
|
|
|
/**
|
|
* Track when the diff request detects no changes
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onNoChanges = function () {
|
|
this.track( 'performance.user.reviewComplete', { duration: ve.now() - this.timings.saveReview } );
|
|
};
|
|
|
|
/**
|
|
* Track whe serilization is complete in review workflow
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onSerializeComplete = function () {
|
|
this.track( 'performance.user.reviewComplete', { duration: ve.now() - this.timings.saveReview } );
|
|
};
|
|
|
|
/**
|
|
* Track when there is a serlization error
|
|
*/
|
|
ve.init.mw.TargetEvents.prototype.onSerializeError = function () {
|
|
if ( this.timings.saveWorkflowBegin ) {
|
|
// This function can be called by the switch to wikitext button as well, so only log
|
|
// reviewError if we actually got here from the save workflow
|
|
this.track( 'performance.user.reviewError', { duration: ve.now() - this.timings.saveReview } );
|
|
}
|
|
};
|