109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)// found in the LICENSE file. 409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)#include "config.h" 609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)#include "platform/animation/TimingFunction.h" 709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 8f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu#include "wtf/MathExtras.h" 9f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu 10c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink { 1109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 1209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)String LinearTimingFunction::toString() const 1309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 1409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return "linear"; 1509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 1609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 1709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)double LinearTimingFunction::evaluate(double fraction, double) const 1809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 1909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return fraction; 2009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 2109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 22197021e6b966cfb06891637935ef33fff06433d1Ben Murdochvoid LinearTimingFunction::range(double* minValue, double* maxValue) const 23197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch{ 24197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch} 25197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 2609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)String CubicBezierTimingFunction::toString() const 2709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 2809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) switch (this->subType()) { 2909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) case CubicBezierTimingFunction::Ease: 3009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return "ease"; 3109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) case CubicBezierTimingFunction::EaseIn: 3209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return "ease-in"; 3309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) case CubicBezierTimingFunction::EaseOut: 3409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return "ease-out"; 3509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) case CubicBezierTimingFunction::EaseInOut: 3609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return "ease-in-out"; 3709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) case CubicBezierTimingFunction::Custom: 3809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return "cubic-bezier(" + String::numberToStringECMAScript(this->x1()) + ", " + 3909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) String::numberToStringECMAScript(this->y1()) + ", " + String::numberToStringECMAScript(this->x2()) + 4009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) ", " + String::numberToStringECMAScript(this->y2()) + ")"; 4109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) default: 4209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) ASSERT_NOT_REACHED(); 4309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) } 4409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return ""; 4509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 4609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 4709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)double CubicBezierTimingFunction::evaluate(double fraction, double accuracy) const 4809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 4909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) if (!m_bezier) 5009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); 5109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return m_bezier->solve(fraction, accuracy); 5209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 5309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 54197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch// This works by taking taking the derivative of the cubic bezier, on the y 55197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch// axis. We can then solve for where the derivative is zero to find the min 56197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch// and max distace along the line. We the have to solve those in terms of time 57197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch// rather than distance on the x-axis 58197021e6b966cfb06891637935ef33fff06433d1Ben Murdochvoid CubicBezierTimingFunction::range(double* minValue, double* maxValue) const 59197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch{ 60197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (0 <= m_y1 && m_y2 < 1 && 0 <= m_y2 && m_y2 <= 1) { 61197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch return; 62197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch } 63197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 64197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double a = 3.0 * (m_y1 - m_y2) + 1.0; 65197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double b = 2.0 * (m_y2 - 2.0 * m_y1); 66197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double c = m_y1; 67197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 68197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (std::abs(a) < std::numeric_limits<double>::epsilon() 69197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch && std::abs(b) < std::numeric_limits<double>::epsilon()) { 70197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch return; 71197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch } 72197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 73197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double t1 = 0.0; 74197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double t2 = 0.0; 75197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 76197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (std::abs(a) < std::numeric_limits<double>::epsilon()) { 77197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch t1 = -c / b; 78197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch } else { 79197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double discriminant = b * b - 4 * a * c; 80197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (discriminant < 0) 81197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch return; 82197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double discriminantSqrt = sqrt(discriminant); 83197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch t1 = (-b + discriminantSqrt) / (2 * a); 84197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch t2 = (-b - discriminantSqrt) / (2 * a); 85197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch } 86197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 87197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double solution1 = 0.0; 88197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double solution2 = 0.0; 89197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 90197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch // If the solution is in the range [0,1] then we include it, otherwise we 91197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch // ignore it. 92197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (!m_bezier) 93197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); 94197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 95197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch // An interesting fact about these beziers is that they are only 96197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch // actually evaluated in [0,1]. After that we take the tangent at that point 97197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch // and linearly project it out. 98197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (0 < t1 && t1 < 1) 99197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch solution1= m_bezier->sampleCurveY(t1); 100197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 101197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch if (0 < t2 && t2 < 1) 102197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch solution2 = m_bezier->sampleCurveY(t2); 103197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 104197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch // Since our input values can be out of the range 0->1 so we must also 105197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch // consider the minimum and maximum points. 106197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double solutionMin = m_bezier->solve(*minValue, std::numeric_limits<double>::epsilon()); 107197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch double solutionMax = m_bezier->solve(*maxValue, std::numeric_limits<double>::epsilon()); 108197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch *minValue = std::min(std::min(solutionMin, solutionMax), 0.0); 109197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch *maxValue = std::max(std::max(solutionMin, solutionMax), 1.0); 110197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch *minValue = std::min(std::min(*minValue, solution1), solution2); 111197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch *maxValue = std::max(std::max(*maxValue, solution1), solution2); 112197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch} 113197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 11409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)String StepsTimingFunction::toString() const 11509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 1167242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci const char* positionString = nullptr; 1177242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci switch (stepAtPosition()) { 1187242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci case Start: 1197242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci positionString = "start"; 1207242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci break; 1217242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci case Middle: 1227242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci positionString = "middle"; 1237242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci break; 1247242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci case End: 1257242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci positionString = "end"; 1267242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci break; 1277242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci } 1287242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 12909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) StringBuilder builder; 1307242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci if (this->numberOfSteps() == 1) { 1317242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci builder.append("step-"); 1327242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci builder.append(positionString); 1337242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci } else { 13409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) builder.append("steps(" + String::numberToStringECMAScript(this->numberOfSteps()) + ", "); 1357242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci builder.append(positionString); 1369e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles) builder.append(')'); 13709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) } 13809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) return builder.toString(); 13909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 14009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 141197021e6b966cfb06891637935ef33fff06433d1Ben Murdochvoid StepsTimingFunction::range(double* minValue, double* maxValue) const 142197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch{ 143197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch *minValue = 0; 144197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch *maxValue = 1; 145197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch} 146197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 14709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)double StepsTimingFunction::evaluate(double fraction, double) const 14809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 149d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) double startOffset = 0; 150d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) switch (m_stepAtPosition) { 1517242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci case Start: 152d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) startOffset = 1; 153d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) break; 1547242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci case Middle: 155d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) startOffset = 0.5; 156d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) break; 1577242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci case End: 158d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) startOffset = 0; 159d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) break; 160d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) default: 161d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) ASSERT_NOT_REACHED(); 162d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) break; 163d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) } 164f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu return clampTo(floor((m_steps * fraction) + startOffset) / m_steps, 0.0, 1.0); 16509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 16609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 167d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)// Equals operators 168d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs) 16909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 170d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return rhs.type() == TimingFunction::LinearFunction; 17109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 17209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 173d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)bool operator==(const CubicBezierTimingFunction& lhs, const TimingFunction& rhs) 17409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){ 175d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) if (rhs.type() != TimingFunction::CubicBezierFunction) 176d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return false; 177d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) 178d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) const CubicBezierTimingFunction& ctf = toCubicBezierTimingFunction(rhs); 179d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) if ((lhs.subType() == CubicBezierTimingFunction::Custom) && (ctf.subType() == CubicBezierTimingFunction::Custom)) 180d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return (lhs.x1() == ctf.x1()) && (lhs.y1() == ctf.y1()) && (lhs.x2() == ctf.x2()) && (lhs.y2() == ctf.y2()); 181d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) 182d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return lhs.subType() == ctf.subType(); 183d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)} 184d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) 185d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs) 186d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles){ 187d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) if (rhs.type() != TimingFunction::StepsFunction) 188d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return false; 189d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) 190d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) const StepsTimingFunction& stf = toStepsTimingFunction(rhs); 1917242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci return (lhs.numberOfSteps() == stf.numberOfSteps()) && (lhs.stepAtPosition() == stf.stepAtPosition()); 192d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)} 193d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) 194d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)// The generic operator== *must* come after the 195d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)// non-generic operator== otherwise it will end up calling itself. 196d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) 197d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles){ 198d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) switch (lhs.type()) { 199d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) case TimingFunction::LinearFunction: { 200d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) const LinearTimingFunction& linear = toLinearTimingFunction(lhs); 201d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return (linear == rhs); 20209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) } 203d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) case TimingFunction::CubicBezierFunction: { 204d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs); 205d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return (cubic == rhs); 206d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) } 207d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) case TimingFunction::StepsFunction: { 208d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) const StepsTimingFunction& step = toStepsTimingFunction(lhs); 209d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return (step == rhs); 210d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) } 211d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) default: 212d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) ASSERT_NOT_REACHED(); 213d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) } 214d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return false; 215d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)} 216d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) 217d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)// No need to define specific operator!= as they can all come via this function. 218d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs) 219d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles){ 220d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return !(lhs == rhs); 22109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)} 22209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 223c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)} // namespace blink 224