Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[weex] Sync recent changes of Weex #6028

Merged
merged 16 commits into from
Jul 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
222 changes: 120 additions & 102 deletions src/platforms/weex/entry-framework.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function init (cfg) {
renderer.Document = cfg.Document
renderer.Element = cfg.Element
renderer.Comment = cfg.Comment
renderer.sendTasks = cfg.sendTasks
renderer.compileBundle = cfg.compileBundle
}

/**
Expand All @@ -35,7 +35,7 @@ export function reset () {
delete renderer.Document
delete renderer.Element
delete renderer.Comment
delete renderer.sendTasks
delete renderer.compileBundle
}

/**
Expand Down Expand Up @@ -66,18 +66,9 @@ export function createInstance (
// Virtual-DOM object.
const document = new renderer.Document(instanceId, config.bundleUrl)

// All function/callback of parameters before sent to native
// will be converted as an id. So `callbacks` is used to store
// these real functions. When a callback invoked and won't be
// called again, it should be removed from here automatically.
const callbacks = []

// The latest callback id, incremental.
const callbackId = 1

const instance = instances[instanceId] = {
instanceId, config, data,
document, callbacks, callbackId
document
}

// Prepare native module getter and HTML5 Timer APIs.
Expand All @@ -102,11 +93,16 @@ export function createInstance (
weex: weexInstanceVar,
// deprecated
__weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line
}, timerAPIs)
callFunction(instanceVars, appCode)
}, timerAPIs, env.services)

if (!callFunctionNative(instanceVars, appCode)) {
// If failed to compile functionBody on native side,
// fallback to 'callFunction()'.
callFunction(instanceVars, appCode)
}

// Send `createFinish` signal to native.
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'createFinish', args: [] }], -1)
instance.document.taskCenter.send('dom', { action: 'createFinish' }, [])
}

/**
Expand All @@ -117,6 +113,7 @@ export function createInstance (
export function destroyInstance (instanceId) {
const instance = instances[instanceId]
if (instance && instance.app instanceof instance.Vue) {
instance.document.destroy()
instance.app.$destroy()
}
delete instances[instanceId]
Expand All @@ -138,7 +135,7 @@ export function refreshInstance (instanceId, data) {
instance.Vue.set(instance.app, key, data[key])
}
// Finally `refreshFinish` signal needed.
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1)
instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, [])
}

/**
Expand All @@ -153,41 +150,51 @@ export function getRoot (instanceId) {
return instance.app.$el.toJSON()
}

const jsHandlers = {
fireEvent: (id, ...args) => {
return fireEvent(instances[id], ...args)
},
callback: (id, ...args) => {
return callback(instances[id], ...args)
}
}

function fireEvent (instance, nodeId, type, e, domChanges) {
const el = instance.document.getRef(nodeId)
if (el) {
return instance.document.fireEvent(el, type, e, domChanges)
}
return new Error(`invalid element reference "${nodeId}"`)
}

function callback (instance, callbackId, data, ifKeepAlive) {
const result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive)
instance.document.taskCenter.send('dom', { action: 'updateFinish' }, [])
return result
}

/**
* Receive tasks from native. Generally there are two types of tasks:
* 1. `fireEvent`: an device actions or user actions from native.
* 2. `callback`: invoke function which sent to native as a parameter before.
* @param {string} instanceId
* @param {array} tasks
* Accept calls from native (event or callback).
*
* @param {string} id
* @param {array} tasks list with `method` and `args`
*/
export function receiveTasks (instanceId, tasks) {
const instance = instances[instanceId]
if (!instance || !(instance.app instanceof instance.Vue)) {
return new Error(`receiveTasks: instance ${instanceId} not found!`)
}
const { callbacks, document } = instance
tasks.forEach(task => {
// `fireEvent` case: find the event target and fire.
if (task.method === 'fireEvent') {
const [nodeId, type, e, domChanges] = task.args
const el = document.getRef(nodeId)
document.fireEvent(el, type, e, domChanges)
}
// `callback` case: find the callback by id and call it.
if (task.method === 'callback') {
const [callbackId, data, ifKeepAlive] = task.args
const callback = callbacks[callbackId]
if (typeof callback === 'function') {
callback(data)
// Remove the callback from `callbacks` if it won't called again.
if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) {
callbacks[callbackId] = undefined
}
export function receiveTasks (id, tasks) {
const instance = instances[id]
if (instance && Array.isArray(tasks)) {
const results = []
tasks.forEach((task) => {
const handler = jsHandlers[task.method]
const args = [...task.args]
/* istanbul ignore else */
if (typeof handler === 'function') {
args.unshift(id)
results.push(handler(...args))
}
}
})
// Finally `updateFinish` signal needed.
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'updateFinish', args: [] }], -1)
})
return results
}
return new Error(`invalid instance id "${id}" or tasks`)
}

/**
Expand Down Expand Up @@ -288,9 +295,7 @@ function createVueModuleInstance (instanceId, moduleGetter) {
* Generate native module getter. Each native module has several
* methods to call. And all the behaviors is instance-related. So
* this getter will return a set of methods which additionally
* send current instance id to native when called. Also the args
* will be normalized into "safe" value. For example function arg
* will be converted into a callback id.
* send current instance id to native when called.
* @param {string} instanceId
* @return {function}
*/
Expand All @@ -300,12 +305,20 @@ function genModuleGetter (instanceId) {
const nativeModule = modules[name] || []
const output = {}
for (const methodName in nativeModule) {
output[methodName] = (...args) => {
const finalArgs = args.map(value => {
return normalize(value, instance)
})
renderer.sendTasks(instanceId + '', [{ module: name, method: methodName, args: finalArgs }], -1)
}
Object.defineProperty(output, methodName, {
enumerable: true,
configurable: true,
get: function proxyGetter () {
return (...args) => {
return instance.document.taskCenter.send('module', { module: name, method: methodName }, args)
}
},
set: function proxySetter (val) {
if (typeof val === 'function') {
return instance.document.taskCenter.send('module', { module: name, method: methodName }, [val])
}
}
})
}
return output
}
Expand All @@ -328,15 +341,17 @@ function getInstanceTimer (instanceId, moduleGetter) {
const handler = function () {
args[0](...args.slice(2))
}

timer.setTimeout(handler, args[1])
return instance.callbackId.toString()
return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
},
setInterval: (...args) => {
const handler = function () {
args[0](...args.slice(2))
}

timer.setInterval(handler, args[1])
return instance.callbackId.toString()
return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
},
clearTimeout: (n) => {
timer.clearTimeout(n)
Expand Down Expand Up @@ -368,50 +383,53 @@ function callFunction (globalObjects, body) {
}

/**
* Convert all type of values into "safe" format to send to native.
* 1. A `function` will be converted into callback id.
* 2. An `Element` object will be converted into `ref`.
* The `instance` param is used to generate callback id and store
* function if necessary.
* @param {any} v
* @param {object} instance
* @return {any}
* Call a new function generated on the V8 native side.
*
* This function helps speed up bundle compiling. Normally, the V8
* engine needs to download, parse, and compile a bundle on every
* visit. If 'compileBundle()' is available on native side,
* the downloding, parsing, and compiling steps would be skipped.
* @param {object} globalObjects
* @param {string} body
* @return {boolean}
*/
function normalize (v, instance) {
const type = typof(v)

switch (type) {
case 'undefined':
case 'null':
return ''
case 'regexp':
return v.toString()
case 'date':
return v.toISOString()
case 'number':
case 'string':
case 'boolean':
case 'array':
case 'object':
if (v instanceof renderer.Element) {
return v.ref
}
return v
case 'function':
instance.callbacks[++instance.callbackId] = v
return instance.callbackId.toString()
default:
return JSON.stringify(v)
function callFunctionNative (globalObjects, body) {
if (typeof renderer.compileBundle !== 'function') {
return false
}
}

/**
* Get the exact type of an object by `toString()`. For example call
* `toString()` on an array will be returned `[object Array]`.
* @param {any} v
* @return {string}
*/
function typof (v) {
const s = Object.prototype.toString.call(v)
return s.substring(8, s.length - 1).toLowerCase()
let fn = void 0
let isNativeCompileOk = false
let script = '(function ('
const globalKeys = []
const globalValues = []
for (const key in globalObjects) {
globalKeys.push(key)
globalValues.push(globalObjects[key])
}
for (let i = 0; i < globalKeys.length - 1; ++i) {
script += globalKeys[i]
script += ','
}
script += globalKeys[globalKeys.length - 1]
script += ') {'
script += body
script += '} )'

try {
const weex = globalObjects.weex || {}
const config = weex.config || {}
fn = renderer.compileBundle(script,
config.bundleUrl,
config.bundleDigest,
config.codeCachePath)
if (fn && typeof fn === 'function') {
fn(...globalValues)
isNativeCompileOk = true
}
} catch (e) {
console.error(e)
}

return isNativeCompileOk
}
8 changes: 4 additions & 4 deletions test/weex/runtime/framework.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ describe('framework APIs', () => {
{ method: 'fireEvent', args: [textRef, 'click'] }
])
expect(result instanceof Error).toBe(true)
expect(result).toMatch(/receiveTasks/)
expect(result).toMatch(/not found/)
expect(result).toMatch(/invalid\sinstance\sid/)
expect(result).toMatch(instance.id)
done()
})
})
Expand Down Expand Up @@ -341,8 +341,8 @@ describe('framework APIs', () => {
{ method: 'callback', args: [callbackId] }
])
expect(result instanceof Error).toBe(true)
expect(result).toMatch(/receiveTasks/)
expect(result).toMatch(/not found/)
expect(result).toMatch(/invalid\sinstance\sid/)
expect(result).toMatch(instance.id)
done()
})
})
Expand Down